链接:http://poj.org/problem?id=3308
题意:未来世界火星人要入侵地球,他们要派一些伞兵来摧毁地球的兵工厂,兵工厂可以视为一个m*n的矩阵,现在知道了他们每个伞兵的降落位置。为了粉碎火星人的阴谋,我们需要在某行或某列来架一个机关枪来消灭一整行或一整列的火星人,但是在这需要一定的花费,告诉每行及每列架机关枪的花费,总花费是每行及每列的花费相乘。求使得火星人全部被消灭的最小花费。
思路:需要消灭所有敌人,是二分图最小点权覆盖问题,要覆盖所有的边并使花费最小,即求最小割,根据最大流最小割定理,问题转换为了求最大流。
建图:建立一个二分图,以行和列为顶点,虚构一个源点和一个汇点,源点向所有行顶点连一条容量为该行花费的弧,所有列顶点向汇点连一条容量为该列花费的弧,对于外星人空降位置,在该行和该列间连一条容量为INF的弧。设所有行顶点集合为R,所有列顶点集合为C,源点为src,汇点为sink,现在图分为三个模块,src→R,R→C,C→sink,这么建图后,R→C容量为INF不可能选为最小割,所以最小割为src→R和C→sink的集合,即选中了行和列,确保了消灭所有火星人。
数据处理:最小花费是乘积,于是可以把每条弧的权取log(),求最大流都加起来之后再exp()就是最后答案。
#include<cstring>
#include<string>
#include<fstream>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<stack>
#include<ctime>
#include<cstdlib>
#include<functional>
#include<cmath>
using namespace std;
#define PI acos(-1.0)
#define MAXN 500100
#define eps 1e-7
#define INF 0x7FFFFFFF
#define LLINF 0x7FFFFFFFFFFFFFFF
#define seed 131
#define mod 1000000007
#define ll long long
#define ull unsigned ll
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
struct node{
int u,next;
double w;
}edge[5000];
int dist[200],head[200];
int n,m,cnt,l,src,sink;
void add_edge(int a,int b,double c){
edge[cnt].u = b;
edge[cnt].w = c;
edge[cnt].next = head[a];
head[a] = cnt++;
}
bool bfs(){
int i,j;
memset(dist,-1,sizeof(dist));
dist[src] = 1;
queue<int>q;
q.push(src);
while(!q.empty()){
int u = q.front();
q.pop();
for(i=head[u];i!=-1;i=edge[i].next){
int v = edge[i].u;
if(dist[v]==-1&&edge[i].w>eps){
dist[v] = dist[u] + 1;
q.push(v);
}
}
}
if(dist[sink]!=-1) return true;
return false;
}
double dfs(int u,double delta){
int i,j;
if(u==sink) return delta;
double dd,ret = 0;
for(i=head[u];i!=-1;i=edge[i].next){
int v = edge[i].u;
if(dist[v]==dist[u]+1&&edge[i].w>eps){
dd = dfs(v,min(edge[i].w,delta));
edge[i].w -= dd;
edge[i^1].w += dd;
delta -= dd;
ret += dd;
}
}
return ret;
}
int main(){
int i,j,t;
double a;
int b,c;
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&m,&n,&l);
memset(head,-1,sizeof(head));
src = 0;
sink = n + m + 1;
cnt = 0;
for(i=1;i<=m;i++){
scanf("%lf",&a);
add_edge(src,i,log(a));
add_edge(i,src,0);
}
for(i=m+1;i<=n+m;i++){
scanf("%lf",&a);
add_edge(i,sink,log(a));
add_edge(sink,i,0);
}
for(i=0;i<l;i++){
scanf("%d%d",&b,&c);
add_edge(b,c+m,INF);
add_edge(c+m,b,0);
}
double ans = 0;
while(bfs()){
ans += dfs(src,INF);
}
ans = exp(ans);
printf("%.4lf\n",ans);
}
return 0;
}