pku 3308 Paratroopers 最大流最小割
http://poj.org/problem?id=3308
题意:火星人要和地球人PK,地球人间谍搞到了一份情报:火星人要搞伞兵,登陆在地球一个row*col的地图上,而且知道伞兵的数量和每个伞兵要降落的格子。为了消灭敌人,可以在某一行或者某一列安置激光枪。每个激光枪可以瞬间消灭这一行(或者列)的敌人。
安装消灭第i行的激光枪消费是ri。
安装消灭第j行的激光枪消费是ci。
现在总部要你花费最小的费用,安装好足够的激光枪去消灭所有的火星人,问最小的花费是多少。
这里花费的定义有点不同:是每个激光器消费的乘积。
思路:最小割_最大流,把伞兵看成边,行列看成节点,转化为了带权二分图最小点覆盖。加入超级源点和超级汇点,源点和所有行节点相连(权值ri),所有列节点和汇点相连(权值ci),如果a行b列有敌人,则把节点a和节点b相连。则问题又可以转化求最小割。
因为对任一敌人<a,b>,必然有source-->a-->b-->sink,故路径上的三条边<source,a>, <a,b>, <b,sink>中至少有一条边在割中,我们把<a,b>的权值设置为无限大,则其不可能被选中。于是割边集中必然有<source,a>和<b,sink>中的至少一条,也即对应选择了相应的行或列,我们把这些边的权值设置为花费,则最小割即是总花费的最小方案。
最小割:对于图中的两个点(一般为源点和汇点)来说,如果把图中的一些边去掉,如果它们之间无法连通的话,则这些边组成的集合就叫为割了。如果这些边有权值,最小割就是指权值之和最小的一个割。
最大流最小割:应用于网络中,指总流量不超过链路可承载的最大值,且在每条子路径上取尽可能少的流量。对任意一个只有一个源点一个汇点的图来说,从源点到汇点的最大流等于最小割。
#include <cstdio> #include <cstring> #include <cmath> #include <queue> #define maxn 107 using namespace std; const double inf = 99999999.0; double c[maxn][maxn]; int level[maxn]; bool vt[maxn]; int n,m,l; bool layer(int s,int e) { int i; queue<int>q; q.push(s); memset(level,-1,sizeof(level)); level[s] = 1; while (!q.empty()) { int p = q.front(); q.pop(); for (i = s; i <= e; ++i) { if (c[p][i] && level[i] == -1) { level[i] = level[p] + 1; if (i == e) return true; q.push(i); } } } return false; } double dinic(int s,int e) { deque<int>q; int pos,i,vs,ve; double maxf = 0; while (layer(s,e)) { memset(vt,false,sizeof(vt)); q.push_back(s); vt[s] = true; while (!q.empty()) { int p = q.back(); if (p == e) { double min = inf; for (i = 1; i < q.size(); ++i) { vs = q[i - 1]; ve = q[i]; if (c[vs][ve] > 0 && min > c[vs][ve]) { min = c[vs][ve]; pos = vs; } } maxf += min; for (i = 1; i < q.size(); ++i) { vs = q[i - 1]; ve = q[i]; if (c[vs][ve] > 0) { c[vs][ve] -= min; c[ve][vs] += min; } } while (!q.empty() && q.back() != pos) { vt[q.back()] = false; q.pop_back(); } } else { for (i = s; i <= e; ++i) { if (c[p][i] > 0 && !vt[i] && level[i] == level[p] + 1) { vt[i] = true; q.push_back(i); break; } } if (i > e) q.pop_back(); } } } return maxf; } int main() { int i,x,y,s,e,t; double val; scanf("%d",&t); while (t--) { memset(c,0,sizeof(c)); scanf("%d%d%d",&n,&m,&l); s = 0; e = n + m + 1; for (i = 1; i <= n; ++i) { scanf("%lf",&val); c[s][i] = log(val); } for (i = 1; i <= m; ++i) { scanf("%lf",&val); c[i + n][e] = log(val); } for (i = 1; i <= l; ++i) { scanf("%d%d",&x,&y); c[x][y + n] = inf; } double ans = dinic(s,e); printf("%.4lf\n",exp(ans)); } return 0; }