POJ 3308 Paratroopers(最大流最小割の最小点权覆盖)

Description

It is year 2500 A.D. and there is a terrible war between the forces of the Earth and the Mars. Recently, the commanders of the Earth are informed by their spies that the invaders of Mars want to land some paratroopers in the m × n grid yard of one their main weapon factories in order to destroy it. In addition, the spies informed them the row and column of the places in the yard in which each paratrooper will land. Since the paratroopers are very strong and well-organized, even one of them, if survived, can complete the mission and destroy the whole factory. As a result, the defense force of the Earth must kill all of them simultaneously after their landing.

In order to accomplish this task, the defense force wants to utilize some of their most hi-tech laser guns. They can install a gun on a row (resp. column) and by firing this gun all paratroopers landed in this row (resp. column) will die. The cost of installing a gun in the ith row (resp. column) of the grid yard is ri (resp. ci ) and the total cost of constructing a system firing all guns simultaneously is equal to the product of their costs. Now, your team as a high rank defense group must select the guns that can kill all paratroopers and yield minimum total cost of constructing the firing system.

Input

Input begins with a number T showing the number of test cases and then, T test cases follow. Each test case begins with a line containing three integers 1 ≤ m ≤ 50 , 1 ≤ n ≤ 50 and 1 ≤ l ≤ 500 showing the number of rows and columns of the yard and the number of paratroopers respectively. After that, a line with m positive real numbers greater or equal to 1.0 comes where the ith number is ri and then, a line with n positive real numbers greater or equal to 1.0 comes where the ith number is ci. Finally, l lines come each containing the row and column of a paratrooper.

Output

For each test case, your program must output the minimum total cost of constructing the firing system rounded to four digits after the fraction point.

 

题目大意:有一个m行n列的矩阵,矩阵上有L个点,每行每列各有一个权值,要求选出若干行和列,覆盖矩阵上所有的点,同时要求这些选出的行和列的权的乘积最小。

思路:首先要求的是乘积最小,利用公式ln(a*b) = lna + lnb,把求乘积转化成求和,在输出答案的时候再来个exp(ans)。这题为最小点权覆盖,建网络流图:从源点S建一条边到每一行,容量为该行的权值的log,从每一列到汇点T建一条边,容量为该权值的log。对每一个点(i, j),从第i行建一条边到第j列,容量为正无穷大。跑最大流,求出最小割,再弄个exp就是答案。

小证明:每一个简单割(只割掉与源点或汇点有关联的边,即不割掉行与列之间的边)都唯一对应着一个可行的方案。记所割掉的与源点关联的点为cut(S),其余与S关联的点为else(S),割掉的与汇点关联的点为cut(T),其余与T关联的点未else(T),我们选上的点就是cut(S)与cut(T)。那么对于cut(S)与cut(T)之间的边,都有点对应(由割的定义,允许有从cut(S)到cut(T)的边);对于else(S)与cut(T)之间的边,都会有一点在cut(T)上;对于else(T)与cut(S)之间的边,都会有一点在cut(S)上;根据割的定义,简单割所对应的方案不会有else(S)到else(T)之间的边。故所有简单割所唯一对应的方案都是合法的。而最小割就对应着那些选上的点的权值之和(题目中为log之和),我们要权值最小,那么最小割就是答案,所以最大流就是答案(exp之后)。

PS:虽然不是每一个方案都对应着一个简单割,但是不对应着简单割的方案,一定多选了几个点……

PS2:此题流量浮点数,所以要注意,EPS据说要选输出精度的两倍,据说这题不用两倍也行。至于我看到有人说,由于精度问题INF不能选太大的,选1e2就够了,不过其实不一定有影响,我的代码就没有影响,INF太大的话增减流量的时候,INF-minFlow精度差太多结果还会是INF,不过它变不变好像跟我没什么关系……反正又不会减到0……

PS3:我现在才发现我的ISAP模板里面不能有点0,之前那题是怎么AC的呢>_<

 

代码(0MS):

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #include <queue>
  5 #include <cmath>
  6 using namespace std;
  7 
  8 const int MAXN = 110;
  9 const int MAXE = 3010;
 10 const double INF = 1e100;
 11 const double EPS = 1e-8;
 12 
 13 inline int sgn(double x) {
 14     if(fabs(x) < EPS) return 0;
 15     return x > 0 ? 1 : -1;
 16 }
 17 
 18 struct SAP {
 19     int head[MAXN], dis[MAXN], cur[MAXN], pre[MAXN], gap[MAXN];
 20     int to[MAXE], next[MAXE];
 21     double flow[MAXE];
 22     int ecnt, n, st, ed;
 23 
 24     void init() {
 25         memset(head, 0, sizeof(head));
 26         ecnt = 2;
 27     }
 28 
 29     void add_edge(int u, int v, double c) {
 30         to[ecnt] = v; flow[ecnt] = c; next[ecnt] = head[u]; head[u] = ecnt++;
 31         to[ecnt] = u; flow[ecnt] = 0; next[ecnt] = head[v]; head[v] = ecnt++;
 32         //printf("%d->%d %lf\n", u, v, c);
 33     }
 34 
 35     void bfs() {
 36         memset(dis, 0x3f, sizeof(dis));
 37         queue<int> que; que.push(ed);
 38         dis[ed] = 0;
 39         while(!que.empty()) {
 40             int u = que.front(); que.pop();
 41             ++gap[dis[u]];
 42             for(int p = head[u]; p; p = next[p]) {
 43                 int &v = to[p];
 44                 if(sgn(flow[p ^ 1]) && dis[v] > n) {
 45                     dis[v] = dis[u] + 1;
 46                     que.push(v);
 47                 }
 48             }
 49         }
 50     }
 51 
 52     double Max_flow(int ss, int tt, int nn) {
 53         st = ss; ed = tt; n = nn;
 54         double ans = 0, minFlow = INF;
 55         for(int i = 0; i <= n; ++i) {
 56             cur[i] = head[i];
 57             gap[i] = 0;
 58         }
 59         int u = pre[st] = st;
 60         bfs();
 61         while(dis[st] < n) {
 62             bool flag = false;
 63             for(int &p = cur[u]; p; p = next[p]) {
 64                 int &v = to[p];
 65                 if(sgn(flow[p]) && dis[u] == dis[v] + 1) {
 66                     flag = true;
 67                     minFlow = min(minFlow, flow[p]);
 68                     pre[v] = u;
 69                     u = v;
 70                     if(u == ed) {
 71                         ans += minFlow;
 72                         while(u != st) {
 73                             u = pre[u];
 74                             flow[cur[u]] -= minFlow;
 75                             flow[cur[u] ^ 1] += minFlow;
 76                         }
 77                         minFlow = INF;
 78                     }
 79                     break;
 80                 }
 81             }
 82             if(flag) continue;
 83             int minDis = n - 1;
 84             for(int p = head[u]; p; p = next[p]) {
 85                 int &v = to[p];
 86                 if(sgn(flow[p]) && dis[v] < minDis) {
 87                     minDis = dis[v];
 88                     cur[u] = p;
 89                 }
 90             }
 91             if(--gap[dis[u]] == 0) break;
 92             ++gap[dis[u] = minDis + 1];
 93             u = pre[u];
 94         }
 95         return ans;
 96     }
 97 } G;
 98 
 99 int main() {
100     int T, n, m, l, a, b;
101     double x;
102     scanf("%d", &T);
103     while(T--) {
104         scanf("%d%d%d", &n, &m, &l);
105         G.init();
106         int ss = n + m + 1, tt = n + m + 2;
107         for(int i = 1; i <= n; ++i) {
108             scanf("%lf", &x);
109             G.add_edge(ss, i, log(x));
110         }
111         for(int i = 1; i <= m; ++i) {
112             scanf("%lf", &x);
113             G.add_edge(i + n, tt, log(x));
114         }
115         for(int i = 1; i <= l; ++i) {
116             scanf("%d%d", &a, &b);
117             G.add_edge(a, b + n, INF);
118             //G.add_edge(b + n, a, INF);
119         }
120         printf("%.4f\n", exp(G.Max_flow(ss, tt, tt)));
121     }
122 }
View Code

 

posted @ 2013-08-10 16:40  Oyking  阅读(404)  评论(0编辑  收藏  举报