POJ_3308
这个题目有点像二分图最小点集覆盖问题,但由于边带权,所以不能直接用二分图最大匹配来做。
参考了别人的想法之后,发现构图的时候原来还是要用的二分图的思想,将行、列分别看做一个集合,由于最后求费用乘积的最小值,所以先传化成log2形式的费用,源点与行之间的容量初始化为log2行费用,列与汇点之间的容量初始化log2列费用。之后对读入的伞兵位置对应的行和列之间连一条有向边,容量为INF。
最后只要求一下最小割就可以了,也就是最大网络流。
#include<stdio.h>
#include<string.h>
#include<math.h>
double cap[110][110],flow[110][110],a[110];
int q[110],p[110];
int main()
{
int i,j,k,n,m,l,T,t,u,v,front,rear;
double f,temp;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&m,&n,&l);
t=m+n+1;
for(i=0;i<=t;i++)
for(j=0;j<=t;j++)
{
cap[i][j]=0.0;
flow[i][j]=0.0;
}
for(i=1;i<=m;i++)
{
scanf("%lf",&temp);
cap[0][i]=log2(temp);
}
for(i=m+1;i<t;i++)
{
scanf("%lf",&temp);
cap[i][t]=log2(temp);
}
for(i=0;i<l;i++)
{
scanf("%d%d",&u,&v);
cap[u][m+v]=1000000000.0;
}
f=0;
while(1)
{
for(i=1;i<=t;i++)
a[i]=0.0;
a[0]=1000000000.0;
front=rear=0;
q[rear++]=0;
while(front<rear)
{
u=q[front++];
for(v=0;v<=t;v++)
if(a[v]<1e-8&&cap[u][v]>flow[u][v])
{
temp=cap[u][v]-flow[u][v];
a[v]=a[u]<temp?a[u]:temp;
p[v]=u;
q[rear++]=v;
}
}
if(a[t]<1e-8)
break;
for(u=t;u!=0;u=p[u])
{
flow[p[u]][u]+=a[t];
flow[u][p[u]]-=a[t];
}
f+=a[t];
}
printf("%.4f\n",pow(2,f));
}
return 0;
}