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

来源:互联网 发布:win7如何安装linux系统 编辑:程序博客网 时间:2024/05/17 15:19
Paratroopers
 
Description

It is year 2500 A.D. and there is a terrible war between theforces of the Earth and the Mars. Recently, the commanders of theEarth are informed by their spies that the invaders of Mars want toland some paratroopers in the m × n grid yard of onetheir main weapon factories in order to destroy it. In addition,the spies informed them the row and column of the places in theyard in which each paratrooper will land. Since the paratroopersare very strong and well-organized, even one of them, if survived,can complete the mission and destroy the whole factory. As aresult, the defense force of the Earth must kill all of themsimultaneously after their landing.

In order to accomplish this task, the defense force wants toutilize some of their most hi-tech laser guns. They can install agun on a row (resp. column) and by firing this gun all paratrooperslanded in this row (resp. column) will die. The cost of installinga gun in the ith row (resp. column) of the grid yard isri (resp. ci ) and the totalcost of constructing a system firing all guns simultaneously isequal to the product of their costs. Now, your team as a high rankdefense group must select the guns that can kill all paratroopersand yield minimum total cost of constructing the firing system.

Input

Input begins with a number T showing the number of testcases and then, T test cases follow. Each test case beginswith a line containing three integers 1 ≤ m ≤ 50 , 1 ≤n ≤ 50 and 1 ≤ l ≤ 500 showing the number of rows andcolumns of the yard and the number of paratroopers respectively.After that, a line with m positive real numbers greater orequal to 1.0 comes where the ith number isri and then, a line with n positive realnumbers greater or equal to 1.0 comes where the ith numberis ci. Finally, l lines come eachcontaining the row and column of a paratrooper.

Output

For each test case, your program must output the minimum totalcost of constructing the firing system rounded to four digits afterthe 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的权值(每列的费用)表示容量. 样例建图如下图 :

      ACM: <wbr>图论题 <wbr>poj <wbr>3308 <wbr>最大流问题

      因为结果要求乘积, 费用就用以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;
}

0 0