您的位置:首页 > 其它

ACM: 图论题 poj 3308 最大流问题

2016-05-19 23:26 459 查看
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.

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

 

题意: 地球和火星发生战争, 火星派特工来偷袭地球的武器, 因为火星特工很厉害, 只要一个

     
生存下来就可以破坏全部. 现在要消灭全部特工, 有消息获得每个特工的降落位置(x,y);

     
地球用激光枪消灭, 每建造一个可以消灭整行或整列, 但是每行每列的造价不一样, 要求

     
用最少费用消灭全部敌人. 造价是全部激光枪的费用的乘积.(这里怪怪: product).

 

解题思路:

    1.
题目其实不难, 仔细分析问题: 尽可能用最小费用, 消灭最多的敌人. 每次行列交叉,

      
都有2个选择. 就像选择路径一样, 但是路径上的敌人会随着你的选择而改变. 图论

      
中网络流问题. 路径上有容量 和 流量.

    2. 果断建图:
设超级源点start = 0, 每行1~n节点X集合, 每列n+1~n+m节点Y集合.

                  
超级汇点end = n+m+1;

      
从start到X集合的权值(每行费用)表示容量, X集合到Y集容量为INF(无限大), Y集合

      
到end的权值(每列的费用)表示容量. 样例建图如下图 :

      



      
因为结果要求乘积, 费用就用以10为底的对数表示方便加法, 最后exp转换回来即可.

    3.
剩下就是求出网络流-最大流, 每条路径最用最小费用限制了, 这样结果最大流就是

      
消灭全部敌人最小费用. KM算法(增广路算法)即可.

 

代码:

#include <cstdio>

#include <iostream>

#include <cstring>

#include <cmath>

#include <queue>

using namespace std;

#define MAX 1005

const int INF = (1<<29);

int n, m, L, p[MAX];

double cap[MAX][MAX], flow[MAX][MAX], a[MAX];

int start, end, M;

inline double min(double a, double b)

{

 return a < b ? a : b;

}

void readGraph()

{

 int i, x, y;

 double w;

 memset(cap, 0, sizeof(cap));

 scanf("%d %d %d", &n,
&m, &L);

 start = 0, end = n+m+1, M = end+1;

 for(i = 1; i <= n; ++i)

 {

  scanf("%lf",
&w);

  cap[start][i] = log(w);

  cap[i][start] = 0;

 }

 for(i = 1; i <= m; ++i)

 {

  scanf("%lf",
&w);

  cap[i+n][end] = log(w);

  cap[end][i+n] = 0;

 }

 for(i = 1; i <= L; ++i)

 {

  scanf("%d %d",
&x, &y);

  cap[x][y+n] = INF;

  cap[y+n][x] = 0;

 }

}

double KM(int start, int end)

{

 double result = 0;

 memset(flow, 0, sizeof(flow));

 queue qu;

 

 while( true )

 {

  memset(p, -1, sizeof(p));

  memset(a, 0, sizeof(a));

  a[start] = INF;

  qu.push(start);

  while( !qu.empty() )

  {

   int u =
qu.front();

   qu.pop();

   for(int v =
0; v < M; ++v)

   {

    if(
!a[v] && cap[u][v] >
flow[u][v])

    {

     p[v]
= u;

     qu.push(v);

     a[v]
= min(a[u], cap[u][v]-flow[u][v]);

    }

   }

  }

  if(a[end] == 0) break;

  for(int u = end; u != start; u
= p[u])

  {

   flow[p[u]][u]
+= a[end];

   flow[u][p[u]]
-= a[end];

  }

  result += a[end];

 }

 return result;

}

int main()

{

// freopen("input.txt", "r", stdin);

 int caseNum;

 scanf("%d", &caseNum);

 while(caseNum--)

 {

  readGraph();

  double result = exp(KM(start,
end));

  printf("%.4lf\n",
result);

 }

 return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: