POJ 3308 Paratroopers【最大流】

题意: 一群伞兵要降落在一个m*n的区域,知道了每个伞兵落入的位置,防守方可以在任意一排或任意一列安装武器,分别可以防御某一排或某一列的敌人,知道了武器放

         某一排或某一列的建造费用,找出一种建造方案使得所有武器花费的乘以最小并足以防御任意一个位置的敌人。

分析:建图方式:

        将每一行看成一个点(1..n),将每一列看成一个点(n..n+m)

        建立一个源点 s = 0, 汇点 t = n+m+1

        在 s 和每个行顶点之间连一条容量为行造价的边

        在每个列顶点和 t 之间连一条容量为列造价的边

        如果(x,y)位置有伞兵,就在 x 行和y列之间连一条容量为无穷大的边

        求出最小割即为最小费用,需要注意的地方是要通过取对数的方式将乘积转化为和。

#include<stdio.h>
#include<string.h>
#include<math.h>
#define clr(x)memset(x,0,sizeof(x))
#define maxn 105
const double INF=999999999.0;
double  c[maxn][maxn];
int gap[maxn];                      // 用 gap 记录当前标号为 i 的个数,
int dis[maxn];                      // 用于 gap 优化
void init(int s,int t,int n)      // 初始化 dis 数组,将图分层
{
    int v,x,q[maxn],front=0,rear=0;
    memset(gap,0,sizeof(gap));
    memset(dis,0xff,sizeof(dis));
    q[rear++]=t;
    dis[t]=0;
    while(front<rear)
    {
        x=q[front++];
        gap[dis[x]]++;
        for(v=0;v<n;v++)            // 从下标的最小值到最大值
        {
            if(dis[v]==-1&&c[v][x]>0)
            {
                dis[v]=dis[x]+1;
                q[rear++]=v;
            }
        }
    }
}
double sap_flow(int s,int t,int n)
{
    init(s,t,n);
    int top=s,i,j,k;     // top 是当前增广路中最前面的一个点
    int pre[maxn];
    double low[maxn];
    double flow=0;
    memset(low,0,sizeof(low));
    while(dis[s]<n)            //  小于总节点数
    {
        int flag=0;
        low[s]=INF;
        for(i=0;i<n;i++)      //  从下标的最小值最大值,有时候最后一个可能是n-1 !!
            if(c[top][i]>0&&dis[top]==dis[i]+1&&dis[i]>=0) // 找允许弧
            {
                flag=1;
                break;
            }
        if(flag)             //  找到允许弧
        {
            low[i]=c[top][i];
            if(low[i]>low[top])
                low[i]=low[top];  // 更新 low 值
            pre[i]=top;
            top=i;
            if(top==t)           // 找到一条增广路
            {
                flow+=low[t];
                j=top;
                while(j!=s)     // 更新残留网络
                {
                    k=pre[j];
                    c[k][j]-=low[t];
                    c[j][k]+=low[t];
                    j=k;
                }
                top=s;          //  再从头找
                memset(low,0,sizeof(low));
            }
        }
        else                    //  如果没有找到允许弧
        {
            int min=n-1;
            for(j=0;j<n;j++)   //  从下标的最小值最大值,有时候最后一个可能是n-1 !!
            {                   //   昭和 top 相邻 dis 最小的点
                if(c[top][j]>0&&dis[j]+1<min&&dis[j]>=0)
                    min=dis[j]+1;
            }
            gap[dis[top]]--;
            if(gap[dis[top]]==0)
                break;
            gap[min]++;
            dis[top]=min;       // 更新 top 距离值
            if(top!=s)          //  回溯找其他的路径
                top=pre[top];
        }
    }
    return flow;
}
int main()
{
    int s,t,T,i,n,m,p,a,b;
    double w;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&p);
        s=0; t=n+m+1;
        clr(c);
        for(i=1;i<=n;i++)
        {
            scanf("%lf",&w);
            c[s][i]=log(w);
        }
        for(i=1;i<=m;i++)
        {
            scanf("%lf",&w);
            c[i+n][t]=log(w);
        }
        for(i=1;i<=p;i++)
        {
            scanf("%d%d",&a,&b);
            c[a][b+n]=INF;
        }
        printf("%.4lf\n",exp(sap_flow(s,t,n+m+2)));
    }
    return 0;
}

 

posted @ 2012-08-23 23:35  'wind  阅读(192)  评论(0编辑  收藏  举报