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 (二分图最小点权覆盖集+输出最小割方案)
题意 有一个图, 两种操作,一种是删除某点的所有出边,一种是删除某点的所有入边,各个点的不同操作分别有一个花费,现在我们想把这个图的边都删除掉,需要的最小花费是多少. 思路 很明显的二分图最小点权覆盖 ...
随机推荐
- easyui 上 datagrid 的表头的checkbox全选时 取消选中 disabled的checkbox
业务需求: 正常情况下,easyui的全选checkbox会选择表中全部的checkbox包括行.及时对checkbox加了disable属性也没有效果.但是现在的业务是当对checkbox加了dis ...
- java 计算器算法脚本
import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; public String Count(Strin ...
- Spring MVC 的核心应用-1
使用Spring MVC实现登录.注销 配置文件applicationcontext-jdbc.xml <?xml version="1.0" encoding=" ...
- ckeditor + ckfinder + oss存储
ckeditor 与 ckfinder 的整合方法 网上有很多,这里我也就不说了. (主要是以前整合的现在忘记咋弄的了0.0) 我这里整合后直接使用js代码 <script type=&quo ...
- 【Android】导航栏(加图片icon)和不同页面的实现(viewpager+tablayout)
先上图,然后说大致步骤,最后再说细节 图片效果:依序点击导航栏左一.左二.中.右二.右一,最后直接滑动页面(不依靠导航栏切换) 大致步骤如下(文末会有完整代码) [1]创建一个类,我这里取名TabBa ...
- Random类与Random方法
class Test{ public static void main(String[] args){ int min=2; //定义随机数的最小值 int max=102; //定义随机数的最大值 ...
- Jetson tx2 串口通信
主要参考了这篇博客:https://blog.csdn.net/zomb1e0117/article/details/85157014 其中需要注意的是最后的时候cutecom端口需要把设备改为:/d ...
- 如何配置 SpaceVim
本文将系统地介绍如何配置 SpaceVim,配置 SpaceVim 主要包括以下几个内容: 设置 SpaceVim 选项 启动/禁用模块 添加自定义插件 添加自定义按键映射以及插件配置 设置Space ...
- Sublime Text 3 新手上路:必要的安裝、設定與基本使用教學
http://blog.miniasp.com/post/2014/01/07/Useful-tool-Sublime-Text-3-Quick-Start.aspx
- Python CSV模块简介
Table of Contents 1. CSV 1.1. 简介 1.2. 字典方式地读写 1.3. 其它 2. 参考资料 CSV csv文件格式是一种通用的电子表格和数据库导入导出格式.最近我调用R ...