现在的位置: 首页 > 综合 > 正文

POJ 3308最大流最小割

2012年09月28日 ⁄ 综合 ⁄ 共 3598字 ⁄ 字号 评论关闭

思路是百度大牛得到的,代码是自己写的(当然用到了模版)

题意:火星人要和地球人PK,地球人间谍搞到了一份情报:火星人要搞伞兵,登陆在地球一个row*col的地图上,而且知道伞兵的数量和每个伞兵要降落的格子。为了消灭敌人,可以在某一行或者某一列安置激光枪。每个激光枪可以瞬间消灭这一行(或者列)的敌人。

安装消灭第i行的激光枪消费是ri。

安装消灭第j行的激光枪消费是ci。

现在总部要你花费最小的费用,安装好足够的激光枪去消灭所有的火星人,问最小的花费是多少。

这里花费的定义有点不同:是每个激光器消费的乘积。

 

思路:最小割_最大流,EK超时,用dinic模板做,不是一般的快。把伞兵看成边,行列看成节点,转化为了带权二分图最小点覆盖。加入超级源点和超级汇点,源点和所有行节点相连(权值ri),所有列节点和汇点相连(权值ci),如果a行b列有敌人,则把节点a和节点b相连。则问题又可以转化求最小割。

因为对任一敌人<a,b>,必然有source-->a-->b-->sink,故路径上的三条边<source,a>,<a,b>,<b,sink>中至少有一条边在割中,我们把<a,b>的权值设置为无限大,则其不可能被选中。于是割边集中必然有<source,a>和<b,sink>中的至少一条,也即对应选择了相应的行或列,我们把这些边的权值设置为花费,则最小割即是总花费的最小方案。

 

最小割:对于图中的两个点(一般为源点和汇点)来说,如果把图中的一些边去掉,如果它们之间无法连通的话,则这些边组成的集合就叫为割了。如果这些边有权值,最小割就是指权值之和最小的一个割。

最大流最小割:应用于网络中,指总流量不超过链路可承载的最大值,且在每条子路径上取尽可能少的流量。对任意一个只有一个源点一个汇点的图来说,从源点到汇点的最大流等于最小割。

 

花费是乘积,那么转化成log之后就是求和了。所以转化为求最小的和是多少。

一个敌人在a行b列,那么他可以被大炮ra,或者cb消灭。

建立二分图:左边是r行,右边是c列。一个敌人在a行b列,就连接左边的点a和右边的点b。这样,一条边就表示了消灭一个敌人。

那么选取一些点,是这些点的权值之和最小,同时覆盖所有的边(敌人),就是我们的答案了。显然,问题转化为:最小点权覆盖。建图如下:

二分图x和y两部分。

一个超级源点s,和x连接,容量是x的权值。

一个超级汇点t,y和t连接,容量是y的权值。

如果x中点a和y中点b有边,连接ab,权值无限大。

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<cmath>
#define INF 10000000
using namespace std;
const int EDGE_NUM = 20001;//边数
const int POINT_NUM = 501;//点数
struct edge
{
int v;//点
int next;//下一边
double value;//当前边流量
}edge[2*EDGE_NUM];//边信息,以邻接表形式存储

int p[POINT_NUM];//p[i]记录最后一条以i为起点的边的id,即以i为起点的最后一条边为edge[p[i]],而edge[p[i]].next则为以i为起点的倒数第二条边,以此类推
int level[POINT_NUM];//level[i]记录i点的层次
int que[POINT_NUM],out[POINT_NUM];//辅助数组
int edgeNumber;

void init()
{
edgeNumber = 0;
memset(p,-1,sizeof(p));
}

inline void addEdge(int from,int to,double value)//添加边,以邻接表形式存储
{
edge[edgeNumber].v = to;
edge[edgeNumber].value = value;
edge[edgeNumber].next = p[from];
p[from] = edgeNumber++;

edge[edgeNumber].v=from;
edge[edgeNumber].value=0;
edge[edgeNumber].next=p[to];
p[to]=edgeNumber++;
}

double Dinic(int source,int sink,int n)
{
int i;
double maxFlow = 0;
while(true)
{
    int head,tail;
    for(i=0;i<n;i++)level[i] = 0;

    level[source] = 1;//源点为第一层
    head = 0;tail = 0;
    que[0] = source;//que这里当队里使用

while(head<=tail)//BFS该剩余图,计算每个可达点层次
{
    int cur = que[head++];
    for(i=p[cur];i!=-1;i=edge[i].next)
    {
        if(edge[i].value>0&&level[edge[i].v]==0)
        {
        level[edge[i].v] = level[cur]+1;
        que[++tail] = edge[i].v;
        }
    }
}


if(level[sink]==0)break;//不存在增广路

for(i=0;i<n;i++)out[i]=p[i];//out[i]动态记录可用边

int q = -1;//q为已经搜索到的点的个数,que存放途径边信息

while(true)//DFS剩余图,查找增广路
{
    if(q<0)//当前路为空
    {
        int cur = out[source];
        for(;cur!=-1;cur=edge[cur].next)//查找第一条边
        {
        if(edge[cur].value>0&&out[edge[cur].v]!=-1&&level[edge[cur].v]==2)//合法第一条边必须满足:1.流量大于0;2.终点有可用边 3:终点层次为2
        break;
        }

        if(cur==-1)break;//找不到第二层,当前剩余图已经没有增广路

        que[++q]=cur;//存入第一条边id
        out[source]=edge[cur].next;
    }

        int curnode = edge[que[q]].v;//当前路的终点

        if(curnode==sink)//找到一条增广路
        {
            double thisflow = edge[que[0]].value;//thisflow为当前增广路的流量
            int index = 0;//标记最小流量边的id
            for(i=1;i<=q;i++)
            {
                if(thisflow>edge[que[i]].value)
                {
                    thisflow=edge[que[i]].value;
                    index = i;
                }
            }

            maxFlow+=thisflow;
          //  cout<<"test"<<endl;
            for(i=0;i<=q;i++)
            {
            edge[que[i]].value-=thisflow;
            edge[que[i]^1].value+=thisflow;//与其方向相反的边
            }

            q = index-1;//查找下一条增广路时可直接使用当前路的前q条边

        }
    else//尚未找到汇点
        {
            int cur = out[curnode];
            for(;cur!=-1;cur=edge[cur].next)
            {
                if(edge[cur].value>0&&out[edge[cur].v]!=-1&&level[edge[cur].v]==level[curnode]+1)
                break;
            }
            if(cur==-1)//没有下一条路
            {
            out[curnode]=-1;//标记当前点的可达边为0
            q--;
            }
            else
            {
            que[++q]=cur;
            out[curnode]=edge[cur].next;//下一次搜索时可达边从edge[cur].next开始查找
            }
        }
}


}

return maxFlow;
}

int main()
{
int Nn,Ff,Dd;
int Case;
scanf("%d",&Case);
double temp;
while(Case--)
{
    scanf("%d%d%d",&Nn,&Ff,&Dd);
    init();

    for(int i=1;i<=Nn;i++)
    {
    scanf("%lf",&temp);
    addEdge(0,i,log(temp));
    }

    for(int i=1;i<=Ff;i++)
    {
    scanf("%lf",&temp);
    addEdge(i+Nn,Nn+Ff+1,log(temp));
    }
int aa,bb;

for(int i=1;i<=Dd;i++)
{
    scanf("%d%d",&aa,&bb);
    addEdge(aa,bb+Nn,INF);
}

printf("%.4lf\n",exp(Dinic(0,Nn+Ff+1,Nn+Ff+2)));
}
return 0;
}

【上篇】
【下篇】

抱歉!评论已关闭.