题目链接:http://poj.org/problem?id=3308
解析:抽象成二分图,左边的点是行,右边的点是列,如果行列之间有入侵者的话,就用边容量为Max的边连接行列点在加入源点(源点连接所有的行点)和汇点(汇点连接所有的列点)。(好吧,╮(╯▽╰)╭,为什么这么建图我也不是太明了,就知道这么建的话可以把问题转换成求最大流问题)
如果源点到汇点之间有通路的话,必然行点集和列点集之间有至少一条路,反应到矩阵中,就是至少有一个机器人没被杀死。所以要使所有机器人都被杀,就得使s,t之间不能有通路,因为要求消耗最小,所以是最小割
所以我们需要求该图的最小割,即是求最大流,然后用EK算法求出来的全是和,题目要求求积,那么存边权的时候就存ln(边权),ln(a)+ln(b) = ln(a*b),最后的话,a*b=e(lna+lnb)
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<queue>
#define min(x,y) x<y?x:y
#define Max 100000000
using namespace std;
double cap[110][110],flow[110];
int m,n,l;
double ans;
int pre[110];
double bfs(int start,int end)
{
queue<int>Q;
while(!Q.empty())
Q.pop();
memset(pre,-1,sizeof(pre));
memset(flow,0,sizeof(flow));
pre[start] = 0;
flow[start] = Max;
Q.push(start);
while(!Q.empty())
{
int temp = Q.front();
Q.pop();
if(temp == end)
break;
for(int i = 0 ; i <= m+n+1;i++)
{
if(cap[temp][i] > 0 && pre[i] == -1)
{
flow[i] = min(flow[temp],cap[temp][i]);
pre[i] = temp;
Q.push(i);
}
}
}
return flow[end];
}
void EK(int start ,int end)
{
while(1)
{
double t = bfs(start,end);
if(t == 0)
break;
for(int k = end; k!= start; k = pre[k])
{
cap[pre[k]][k] -= t;
cap[k][pre[k]] += t;
}
ans += t;
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(cap,0,sizeof(cap));
scanf("%d%d%d",&m,&n,&l);
for(int i = 1 ; i <= m ; i ++) //1->m是行点,也是二分图的左边点集,全都连到源点0上
{
double a;
scanf("%lf",&a);
cap[0][i] = log(a);
}
for(int i = 1; i <= n ; i ++) //m+1->m+n是列点,也是二分图的右点集,连接到汇点m+n+1上
{
double a;
scanf("%lf",&a);
cap[m+i][m+n+1] = log(a);
}
for(int i = 1; i <= l ; i ++)
{
int a,b;
scanf("%d%d",&a,&b);
cap[a][m+b] = Max;
}
ans = 0;
EK(0,m+n+1);
printf("%.4lf\n",exp(ans));
}
return 0;
}