POJ 3308

//题目类型:最大流最小割
解题思路:把伞兵看成边,行列看成节点,转化为了带权二分图最小点覆盖。
加入超级源点和超级汇点,源点和所有行节点相连,所有列节点和汇点相连,如果a行b列有敌人,则把节点a和节点b相连。则问题又可以转化求最小割。
现在求源点和汇点之间的最小割。因为对任一敌人<a,b>,必然有source-->a-->b-->sink, 割的性质是"不存在一条从source到sink的路径", 故路径上
的三条边<source,a>, <a,b>, <b,sink>中至少有一条边在割中,我们把<a,b>的权值设置为无限大,则其不可能被选中。于是割边集中必然有<source,a>
和<b,sink>中的至少一条,也即对应选择了相应的行或列,我们把这些边的权值设置为花费,则最小割即是总花费的最小方案。还有一个需要注意的地方
是,这里问题是要求cost的乘积,可以通过使用log()把乘法先转换为加法,最后输出的时候再转换回去。
题目分析转载自:http://blog.csdn.net/logic_nut/archive/2009/08/29/4496579.aspx
#include <iostream>
//#include <conio.h>
#include <queue>
#include <math.h>
using namespace std;
#define nodearray 110
const int INF = 1000000;
int m,n,l;
double capacity[nodearray][nodearray];
 
int d[nodearray];           //标号
int num[nodearray];         //num[i]表示标号为i的顶点数有多少
int pre[nodearray];         //记录前驱
int vn;
void ini_d(int src,int des) //BFS计算标号,汇点t标号为0
{
    int k;
    queue<int>Q;
 
    memset(d,1,sizeof(d));         
    memset(num,0,sizeof(num));
 
    Q.push(des);
    d[des]=0;                   //汇点的标号为0
    num[0]=1;
    while (!Q.empty())
    {
        k=Q.front(),Q.pop();
        for (int i=0;i<vn;i++)       //遍历所有的结点
        {
            if (d[i]>=vn&&capacity[i][k]>0) //此处要特别注意,通过frontint的值改变其他的距离标号
            {
                d[i]=d[k]+1;
                Q.push(i);
                num[d[i]]++;
            }
        }
    }
}
 
int findAlowArc(int i)       //从i出发寻找允许弧
{
    int j;
    for (j=0;j<vn;j++) if (capacity[i][j]>0&&d[i]==d[j]+1) return j;
 
    return -1;
}
 
int reLable(int i)         //重新标号
{
    int mm=INF;
    for (int j=0;j<vn;j++)
        if (capacity[i][j]>0) mm=min(mm,d[j]+1);
 
    return mm==INF?vn:mm;
}
 
double maxFlow(int s,int t)      //从源点s出发的最大流
{
    double flow=0;
    int i=s,j;
    double delta;              //增量
 
    memset(pre,-1,sizeof(pre));
    while (d[s]<vn)
    {
        j=findAlowArc(i);
        if (j>=0)
        {
            pre[j]=i;                    
            i=j;                //从前往后找
            if (i==t)           //更新残留网络
            {
                delta=INF;
                for (i=t;i!=s;i=pre[i]) delta=min(delta,capacity[pre[i]][i]); //找到增广路径的增量
                for (i=t;i!=s;i=pre[i]) capacity[pre[i]][i] -= delta, capacity[i][pre[i]] += delta; //更改流量
                flow += delta;
            }
        }
        else
        {
            int x=reLable(i);       //重新标号
            num[x]++;
            num[d[i]]--;
            if (num[d[i]]==0) return flow;      //间隙优化
            d[i]=x;
            if (i!=s) i=pre[i];
        }
    }
    return flow;
}

int main()
{
    //freopen("1.txt","r",stdin);
    int i,j;
    int a,b;
    int src,des;
    int t;
    double cost;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&m,&n,&l);
        src = 0;
        des = m+n+1;
        vn = m+n+2;
        memset(capacity,0,sizeof(capacity));
        for(i=1;i<=m;++i)
        {
            scanf("%lf",&cost);
            capacity[src][i]=log(cost);
        }
        for(i=1;i<=n;++i)
        {
            scanf("%lf",&cost);
            capacity[m+i][des]=log(cost);
        }
        for(i=1;i<=l;++i)
        {
            scanf("%d%d",&a,&b);
            capacity[a][b+m]=INF;
        }
        ini_d(src,des);
        printf("%.4lf\n",exp(maxFlow(src,des)));
    }
    //getch();
    return 0;
}

你可能感兴趣的:(poj)