题意:火星人要攻打地球,火星人将派来一些伞兵来破坏军事设施。已知他们将降落在一个矩阵行的地区,而且知道这些人会降落在哪一行那一列。现在地球人可以造一些大炮来攻击他们,一门大炮可以攻击一行或者一列,一个伞兵只要被他所在的行或列的大炮打中他就会挂,而在每一行、每一列造大炮的价格是不同的,建造大炮总价格等于你选择在某行或某列造大炮价格的乘积。现在要求把所有的伞兵都搞挂了并且让这个价格尽可能的小。
看到乘积尽量小,想到了取对数。那么剩下的问题就是二分图点权覆盖的问题了。最后求完最大流之后,再还原这个费用就可以了。
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <algorithm> 5 using namespace std; 6 const int maxn = 110; 7 const int maxm = 10000; 8 const double inf = 1e8; 9 int v[maxm],next[maxm]; 10 double w[maxm]; 11 int first[maxn],d[maxn],work[maxn],q[maxn]; 12 int e,S,T; 13 14 void init(){ 15 e = 0; 16 memset(first,-1,sizeof(first)); 17 } 18 19 void add_edge(int a,int b,double c){ 20 //printf("add:%d to %d,cap = %.4f ",a,b,c); 21 v[e] = b;next[e] = first[a];w[e] = c;first[a] = e++; 22 v[e] = a;next[e] = first[b];w[e] = 0;first[b] = e++; 23 } 24 25 int bfs(){ 26 int rear = 0; 27 memset(d,-1,sizeof(d)); 28 d[S] = 0;q[rear++] = S; 29 for(int i = 0;i < rear;i++){ 30 for(int j = first[q[i]];j != -1;j = next[j]) 31 if(w[j] && d[v[j]] == -1){ 32 d[v[j]] = d[q[i]] + 1; 33 q[rear++] = v[j]; 34 if(v[j] == T) return 1; 35 } 36 } 37 return 0; 38 } 39 40 double dfs(int cur,double a){ 41 if(cur == T) return a; 42 for(int &i = work[cur];i != -1;i = next[i]){ 43 if(w[i] && d[v[i]] == d[cur] + 1) 44 if(double t = dfs(v[i],min(a,w[i]))){ 45 w[i] -= t;w[i^1] += t; 46 return t; 47 } 48 } 49 return 0; 50 } 51 52 double dinic(){ 53 double ans = 0; 54 while(bfs()){ 55 memcpy(work,first,sizeof(first)); 56 while(double t = dfs(S,inf)) ans += t; 57 } 58 return ans; 59 } 60 61 int main() 62 { 63 int kase,m,n,l; 64 scanf("%d",&kase); 65 while(kase--){ 66 init(); 67 scanf("%d%d%d",&m,&n,&l); 68 S = 0,T = m+n+1; 69 for(int i = 1;i <= m;i++){ 70 double c; 71 scanf("%lf",&c); 72 add_edge(S,i,log(c)); 73 } 74 for(int i = m+1;i <= m+n;i++){ 75 double c; 76 scanf("%lf",&c); 77 add_edge(i,T,log(c)); 78 } 79 for(int i = 0;i < l;i++){ 80 int a,b; 81 scanf("%d%d",&a,&b); 82 add_edge(a,b+m,inf); 83 } 84 double ans = dinic(); 85 printf("%.4f ",exp(ans)); 86 } 87 return 0; 88 }