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):
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std; const int MAXN = ;
const int MAXE = ;
const double INF = 1e100;
const double EPS = 1e-; inline int sgn(double x) {
if(fabs(x) < EPS) return ;
return x > ? : -;
} struct SAP {
int head[MAXN], dis[MAXN], cur[MAXN], pre[MAXN], gap[MAXN];
int to[MAXE], next[MAXE];
double flow[MAXE];
int ecnt, n, st, ed; void init() {
memset(head, , sizeof(head));
ecnt = ;
} void add_edge(int u, int v, double c) {
to[ecnt] = v; flow[ecnt] = c; next[ecnt] = head[u]; head[u] = ecnt++;
to[ecnt] = u; flow[ecnt] = ; next[ecnt] = head[v]; head[v] = ecnt++;
//printf("%d->%d %lf\n", u, v, c);
} void bfs() {
memset(dis, 0x3f, sizeof(dis));
queue<int> que; que.push(ed);
dis[ed] = ;
while(!que.empty()) {
int u = que.front(); que.pop();
++gap[dis[u]];
for(int p = head[u]; p; p = next[p]) {
int &v = to[p];
if(sgn(flow[p ^ ]) && dis[v] > n) {
dis[v] = dis[u] + ;
que.push(v);
}
}
}
} double Max_flow(int ss, int tt, int nn) {
st = ss; ed = tt; n = nn;
double ans = , minFlow = INF;
for(int i = ; i <= n; ++i) {
cur[i] = head[i];
gap[i] = ;
}
int u = pre[st] = st;
bfs();
while(dis[st] < n) {
bool flag = false;
for(int &p = cur[u]; p; p = next[p]) {
int &v = to[p];
if(sgn(flow[p]) && dis[u] == dis[v] + ) {
flag = true;
minFlow = min(minFlow, flow[p]);
pre[v] = u;
u = v;
if(u == ed) {
ans += minFlow;
while(u != st) {
u = pre[u];
flow[cur[u]] -= minFlow;
flow[cur[u] ^ ] += minFlow;
}
minFlow = INF;
}
break;
}
}
if(flag) continue;
int minDis = n - ;
for(int p = head[u]; p; p = next[p]) {
int &v = to[p];
if(sgn(flow[p]) && dis[v] < minDis) {
minDis = dis[v];
cur[u] = p;
}
}
if(--gap[dis[u]] == ) break;
++gap[dis[u] = minDis + ];
u = pre[u];
}
return ans;
}
} G; int main() {
int T, n, m, l, a, b;
double x;
scanf("%d", &T);
while(T--) {
scanf("%d%d%d", &n, &m, &l);
G.init();
int ss = n + m + , tt = n + m + ;
for(int i = ; i <= n; ++i) {
scanf("%lf", &x);
G.add_edge(ss, i, log(x));
}
for(int i = ; i <= m; ++i) {
scanf("%lf", &x);
G.add_edge(i + n, tt, log(x));
}
for(int i = ; i <= l; ++i) {
scanf("%d%d", &a, &b);
G.add_edge(a, b + n, INF);
//G.add_edge(b + n, a, INF);
}
printf("%.4f\n", exp(G.Max_flow(ss, tt, tt)));
}
}
POJ 3308 Paratroopers(最大流最小割の最小点权覆盖)的更多相关文章
- POJ 3308 Paratroopers (对数转换+最小点权覆盖)
题意 敌人侵略r*c的地图.为了消灭敌人,可以在某一行或者某一列安置超级大炮.每一个大炮可以瞬间消灭这一行(或者列)的敌人.安装消灭第i行的大炮消费是ri.安装消灭第j行的大炮消费是ci现在有n个敌人 ...
- poj 3308 Paratroopers(二分图最小点权覆盖)
Paratroopers Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 8954 Accepted: 2702 Desc ...
- POJ - 2125 Destroying The Graph (最小点权覆盖)
题意:给一张图,现在要删去所有的边,删去一个点的所有入边和所有出边都有其对应\(W_{i+}\)和\(W_{i-}\).求删去该图的最小花费,并输出解 分析:简而言之就是用最小权值的点集去覆盖所有的边 ...
- poj3308 Paratroopers 最大流 最小点权覆盖
题意:有一个n*m的矩阵,告诉了在每一行或者每一列安装大炮的代价,每一个大炮可以瞬间消灭这一行或者这一列的所有敌人,然后告诉了敌人可能出现的L个坐标位置,问如何安置大炮,使花费最小.如果一个敌人位于第 ...
- POJ - 3308 Paratroopers (最小点权覆盖)
题意:N*M个格点,K个位置会有敌人.每行每列都有一门炮,能打掉这一行(列)上所有的敌人.每门炮都有其使用价值.总花费是所有使用炮的权值的乘积.求最小的总花费. 若每门炮的权值都是1,就是求最小点覆盖 ...
- POJ 3308 Paratroopers(最小点权覆盖)(对数乘转加)
http://poj.org/problem?id=3308 r*c的地图 每一个大炮可以消灭一行一列的敌人 安装消灭第i行的大炮花费是ri 安装消灭第j行的大炮花费是ci 已知敌人坐标,同时消灭所有 ...
- poj 3308(最小点权覆盖、最小割)
题目链接:http://poj.org/problem?id=3308 思路:裸的最小点权覆盖,建立超级源点和超级汇点,将源点与行相连,容量为这行消灭敌人的代价,将列与汇点相连,容量为这列消灭敌人的代 ...
- POJ3308 Paratroopers(最小割/二分图最小点权覆盖)
把入侵者看作边,每一行每一列都是点,选取某一行某一列都有费用,这样问题就是选总权最小的点集覆盖所有边,就是最小点权覆盖. 此外,题目的总花费是所有费用的乘积,这时有个技巧,就是取对数,把乘法变为加法运 ...
- POJ 2125 Destroying The Graph (二分图最小点权覆盖集+输出最小割方案)
题意 有一个图, 两种操作,一种是删除某点的所有出边,一种是删除某点的所有入边,各个点的不同操作分别有一个花费,现在我们想把这个图的边都删除掉,需要的最小花费是多少. 思路 很明显的二分图最小点权覆盖 ...
随机推荐
- jQuery入门简单实现反选与全选
//html代码<input type="checkbox" id= 'all' value="全选"> 选择全部 一键上路 <input t ...
- Linux中文件函数(一)
一.stat.fstat.fstatat.lstat函数 函数的原型为: #include <sys/stat.h> int stat(const char *restrict pathn ...
- ubnutu 安装protocol buffer
工作中需要使用protocol buffer,需要编译出protocol buffer的动态链接库,然后在别的makefile中链接它, 我的环境是ubnutu16.04,64bit,使用的proto ...
- C语言的乱七八糟
Note For C Linux下C编程基础(gcc/gdb/make使用) 一.vi学习 二.初探emacs 三.gcc编译器 3.1 gcc所支持后缀名解释 后缀名 解释 后缀名 解释 .c C原 ...
- js 校验身份证号
根据地区编码.身份证格式.18位身份证需要验证最后一位校验位 //校验身份证 function IdentityCodeValid(code) { var city = { 11: "北京& ...
- Asp.Net Core使用Log4Net优化日志【项目开源】
我在前一篇文章中介绍了一种使用Log4Net的方法,但是那种方法打出来的日志不是很直观 然后我前不久阅读了一篇非常不错的博客:https://www.cnblogs.com/guolianyu/p/9 ...
- vue的细节
1.如果使用路由跳转到别的界面的话,例如从文章list页面跳转到具体文章查看详情页,查看某一个具体就需要传递那个文章的id去后台查询, this.$router.push的params方法可以实现传递 ...
- 企业SVN版本管理与代码上线方案
1.SVN服务实战 1) 什么是SVN(Subversion)? Svn(subversion)是近年来崛起的非常优秀的版本管理工具,与CVS管理工具一样,SVN是一个跨平台的开源的版本控制系统.Sv ...
- 快排(golang实现) 递归方法
递归方法,逻辑简洁清晰.这个算法还是很重要的,需要重点记忆理解,面试经常考手写.据说是与傅里叶变换等并称“20世纪十大算法”.https://blog.csdn.net/v_JULY_v/articl ...
- linux下Intellij Idea 14的安装
一.安装配置jdk 虽然很多Linux发行版现在已经自带OpenJDK,但是在开发过程中 ...