题意:有一个n*m的矩阵,告诉了在每一行或者每一列安装大炮的代价,每一个大炮可以瞬间消灭这一行或者这一列的所有敌人,然后告诉了敌人可能出现的L个坐标位置,问如何安置大炮,使花费最小。如果一个敌人位于第r行c列,则他可以被第r行或者第c列的大炮消灭。
链接:http://poj.org/problem?id=3308
这题突然就让我想起来那个多校的那道多米诺骨牌的那个,几乎一样的模型,还不过要把权值改一下,因为权值是相乘,那么就吧权值改成相加,也就是用log去改变。
这样的话 如果坐标为x,y的点要被消灭,那么要么走x要么走y,这样的话就直接是一个二分图的形式了。一边是行,一边是列,用出现的点连边,就是最小点权模型,最小点权模型就是集合x点的点权做为s-x的边权(流量),y点的权值为y-t的边权,x-y的边权为无穷大,这样见图那么就可以用最大流搞。
这是我曾经看过的一个大神对于二分图边权覆盖的讲解。好像就是这道题。
求二分图顶点覆盖问题,都是转化为最小割问题去求解,转化方法如下:
建超级源S 和超级汇 T,假设二分图两个点集分别为 X 和 Y。X和Y原来的边容量设为INF,将S到X里每个点x连一条边,边权为x的点权,也可以看做覆盖点x的所有出边的花费(W-),将Y里每个点y到T连一条边,边权为y的点权,也可以看做覆盖点y的所有入边的花费(W+)。这样求得最小割容量,即为原问题的解。
/************这是对这个转化方法的解释和证明,有兴趣的同学可以看看************/
X到Y的边权为INF,自然不会成为最小割中的边,那就只有可能是S到X和Y到T中的边,而:S到X中点x的边e1, 权为点x的点权,点x和Y中的所有临边e2,都需要受到e1的流量的限制,同样,X到Y中点y的所有边也会受到点y到T的容量限制。这样求得割就能保证覆盖掉所有的边。
我们可以用反证法证明一下:假设有边<x, y>没有被覆盖掉,则边<S, x>流量为0且边<y, T>流量为0,而<x, y>流量为INF,自然可以找到一条S到T的增流路径<S, x, y, T>,与以求得流为最大流相矛盾,则可以说明,在最大流的情况下,所有的边都已经被覆盖掉。
而最小割问题可以用最大流来解决,问题就变得简单了。
/****************************************************************************/

1 #include <stdio.h> 2 #include <iostream> 3 #include <queue> 4 #include <string.h> 5 #include <vector> 6 #include <cmath> 7 using namespace std; 8 const double maxn = 1000000.0; 9 int m,n,l; 10 double row[110],col[105]; 11 struct edge 12 { 13 int u,v; 14 double cap,flow; 15 }; 16 vector<edge>edges; 17 vector<edge>ed; 18 vector<int>g[505],G[505]; 19 20 void addedge(int u,int v,double cap,double flow) 21 { 22 edges.push_back((edge){u,v,cap,flow}); 23 edges.push_back((edge){v,u,0,0}); 24 int m; 25 m = edges.size(); 26 g[u].push_back(m-2); 27 g[v].push_back(m-1); 28 } 29 30 void init(int n) 31 { 32 int i; 33 for(i = 0;i <= n;i++) 34 { 35 g[i].clear(); 36 } 37 edges.clear(); 38 } 39 int vis[505],dis[505],cur[505]; 40 int bfs(int s,int t,int n) 41 { 42 memset(vis,0,sizeof(vis)); 43 queue<int>q; 44 q.push(s); 45 dis[s] = 0; 46 vis[s] = 1; 47 48 while(!q.empty()) 49 { 50 int u,v; 51 u = q.front(); 52 q.pop(); 53 for(int i = 0;i < g[u].size();i++) 54 { 55 edge &e = edges[g[u][i]]; 56 v = e.v; 57 if(!vis[v] && e.cap-e.flow > 0) 58 { 59 vis[v] = 1; 60 dis[v] = dis[u] +1; 61 q.push(v); 62 } 63 } 64 } 65 return vis[t]; 66 } 67 double dfs(int u,double a,int t) 68 { 69 if(u == t || a == 0) 70 return a; 71 int v; 72 double flow = 0,f; 73 for(int& i = cur[u]; i < g[u].size();i++) 74 { 75 edge &e = edges[g[u][i]]; 76 v = e.v; 77 if(dis[u]+1 == dis[v] &&(f = dfs(v,min(a,e.cap-e.flow),t))) 78 { 79 e.flow += f; 80 edges[g[u][i]^1].flow -= f; 81 flow += f; 82 a -= f; 83 if(a == 0) 84 break; 85 } 86 } 87 88 return flow; 89 } 90 double maxflow(int s,int t) 91 { 92 double flow = 0; 93 while(bfs(s,t,t)) 94 { 95 memset(cur,0,sizeof(cur)); 96 flow+=dfs(s,1000050,t); 97 } 98 return flow; 99 } 100 101 102 int main() 103 { 104 int i,j; 105 int t; 106 107 while(~scanf("%d",&t)) 108 { 109 while(t--) 110 { 111 scanf("%d %d %d",&n,&m,&l); 112 init(n+m+2); 113 114 for(i=1;i <= n;i++) 115 scanf("%lf",&row[i]); 116 117 for(i = 1;i <= m;i++) 118 scanf("%lf",&col[i]); 119 120 int u,v; 121 122 for(i = 1;i <= n;i++) 123 addedge(0,i,log(row[i]),0); 124 for(i = 1;i <= m;i++) 125 addedge(n+i,n+m+1,log(col[i]),0); 126 127 for(i = 1;i <= l;i++) 128 { 129 int a,b; 130 scanf("%d %d",&a,&b); 131 addedge(a,n+b,maxn,0); 132 } 133 134 double ans = maxflow(0,n+m+1); 135 printf("%.4f ",exp(ans)); 136 } 137 } 138 139 return 0; 140 }