POJ 3308 Paratoopers 【网络流 最小点覆盖】
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 3582 | Accepted: 1104 |
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.
Sample Input
1
4 4 5
2.0 7.0 5.0 2.0
1.5 2.0 2.0 8.0
1 1
2 2
3 3
4 4
1 4
Sample Output
16.0000
Source
solution:
如果不考虑题目中选择每排(列)的费用,那么这题就是一个木有任何掩饰的最小点覆盖问题。
那么就从该模型出发,给它加上点权;对于点权,我们的一般手段就是拆点,转化成网络流的最大流最小割问题。显然,如果这题是求最小和的话,那么建图后求最小割即为答案。
但是既然是求积,那么按照传统思路,求积转化为求和的方法就是取对数,log2()之。
最后这就是一个边容量为浮点型的最大流最小割问题,流之,即AC。
注意点:
1.别脑残建错图,源--(inf)--> 行点 --(log2(val))-->行点拆 --inf--> 列点拆 --(log2(val)) --> 列点 --inf-->汇。。。。不解释,建图2了,调了半天莫名其妙的地方,三天没敲代码就是得死人啊,手生必死!!!
另外建图的时候,别以为你建的图,就“必然真实的表达了你的想法”。
2.double精度的问题,据计算,double精度在15~16位左右,那么本题的inf和eps的差距就不能超过15位,否则精度问题会把你WA成sb。
而由于,eps要开到输出小数位数两倍的原则,那么eps在本题中开到1e-8就是很有必要的一件事情,所以相应的inf最多只能开到1e8。
(但其实呢,2倍原则一般针对带有乘除法的浮点型问题,所以本题只开到1e-5或者1e-6亦可)
但注意本题中的另一个性质,取对数,任何数字log2之后就会变得很小(别告诉我你不知道O(logN)的算法有多么快),所以这题的inf开的很小就好,反正很轻易的就能比单次流上限要高。(inf开到1e2都能过,说明poj还比较仁慈,没有添加什么超2^100的数据。。)
3.poj的C++ 和G++的问题。坑爹啊。G++上面输出double要用%f,C++上面用log2会CE,必须蛋疼的用log(val)/log(2.0),还有就是这个log中的参数必须严格为浮点数,还不能期待int型自动转换过去,唉,又是个CE。。
这题真是CE+WA专题。。全是蛋疼的罚时。
附代码:
#include < cstdio >
#include < cstring >
using namespace std;
#include < cmath >
#define maxn 210
#define maxm 2000
const double inf = 1e2;
const double eps = 1e - 6 ;
struct edge
{
int p,next,anti;
double val;
edge(){}
edge( int _p, int _next, double _val, int _anti):p(_p),next(_next),val(_val),anti(_anti){}
}v[maxn],e[maxm],arc[maxn],path[maxn];
int flow[maxn],pre[maxn],d[maxn],cnt[maxn],tot,n,N,M;
int comp( double x)
{
if (fabs(x) < eps)
return 0 ;
else if (x < - eps)
return - 1 ;
else
return 1 ;
}
void init()
{
tot = 0 ;
n = 2 * (N + M) + 1 ;
for ( int i = 0 ;i <= n;i ++ )
v[i].next = - 1 ;
memset(d, 0 , sizeof (d));
memset(cnt, 0 , sizeof (cnt));
cnt[ 0 ] = n + 1 ;
}
void add( int p, int q, double val)
{
e[tot] = edge(q,v[p].next,val,tot + 1 );
v[p].next = tot ++ ;
e[tot] = edge(p,v[q].next, 0 ,tot - 1 );
v[q].next = tot ++ ;
}
double mflow()
{
int s = 0 ,t = n;
int i,k,least,loc;
double total,now;
bool flag;
for (i = 0 ;i <= n;i ++ )arc[i].next = v[i].next;
for (total = 0 ,i = s,now = inf;d[s] < n + 1 ;)
{
flow[i] = now;
for (flag = false ,k = arc[i].next; ~ k;k = e[k].next)
{
if (comp(e[k].val) > 0 && d[i] == d[e[k].p] + 1 )
{
now = min(e[k].val,now);
pre[e[k].p] = i;
arc[i].next = k;
path[e[k].p].next = k;
i = e[k].p;
if (i == t)
{
for (total += now;i != s;i = pre[i])
{
e[path[i].next].val -= now;
e[e[path[i].next].anti].val += now;
}
now = inf;
}
flag = true ;
break ;
}
}
if ( ! flag)
{
for (least = n + 1 ,k = v[i].next; ~ k;k = e[k].next)
{
if (comp(e[k].val) > 0 && least > d[e[k].p])
{
loc = k;
least = d[e[k].p];
}
}
arc[i].next = loc;
cnt[d[i]] -- ;
if ( ! cnt[d[i]])
break ;
d[i] = least + 1 ;
cnt[d[i]] ++ ;
if (i != s)
{
i = pre[i];
now = flow[i];
}
}
}
return total;
}
void gao()
{
int l;
scanf( " %d %d %d " , & N, & M, & l);
init();
for ( int i = 1 ;i <= N;i ++ )
{
double val;
scanf( " %lf " , & val);
add( 0 ,i,inf);
add(i,i + N,log(val) / log( 2.0 ));
}
for ( int i = 1 ;i <= M;i ++ )
{
double val;
scanf( " %lf " , & val);
add(N + N + i,N + N + M + i,log(val) / log( 2.0 ));
add(N + N + M + i,n,inf);
}
for ( int i = 0 ;i < l;i ++ )
{
int a,b;
scanf( " %d %d " , & a, & b);
add(a + N,b + N + N,inf);
}
double ans = mflow();
printf( " %.4lf\n " ,pow( 2.0 ,ans));
}
int main()
{
int t;
scanf( " %d " , & t);
for ( int i = 0 ;i < t;i ++ )gao();
}