1. 程式人生 > >(POJ3308)Paratroopers 最大流最小割,建圖,模板題

(POJ3308)Paratroopers 最大流最小割,建圖,模板題

                       Paratroopers
        Time Limit: 1000MS      Memory Limit: 65536K
        Total Submissions: 8373     Accepted: 2516

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

Amirkabir University of Technology Local Contest 2006

題意:
在一個n*m的網格中,每行每列有一個鐳射槍,每個鐳射槍可以消滅該行或該列的所有傘兵,使用每個鐳射槍有一定的費用。有L個傘兵位於網格中,要消滅所有的傘兵,要建一個鐳射槍系統,系統的費用是每個鐳射槍的之積。問如何建立鐳射槍系統使整個系統費用最小。

分析:
此題的難點就是建圖,我們以以下方式建圖:把傘兵視邊,行和列視為頂點,增加兩個頂點-源點和匯點;對於第i行,從源點向頂點Ri建立一條容量為在第i行安裝鐳射槍的費用的弧;對於第j列,從頂點Cj向匯點建立一條容量為在第j列安裝鐳射槍的費用的弧。如果有傘兵降落於(i,j),則從頂點Ri向頂點Cj建立一條容量為無窮大的弧。

為了求割的最小容量,所以R->C不可能被選中,只能選中某些行或列。此時最小割即為最小費用。由最大流最小割定理我們求最大流即可。
由於費用是個費用之積,我們可以利用log(),轉換成求和後在還原:a*b*c=e^(log(a*b*c)) ,又log(a*b*c)=log(a)+log(b)+log(c),所以我們先求log(a)+log(b)+log(c),最後利用exp()還原。

AC程式碼:

#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;

#define N 1000
#define INF 100000000

struct Edge
{
    int from,to;
    double cap,flow;
    Edge(int u,int v,double c,double f):from(u),to(v),cap(c),flow(f){}
};

struct Dinic
{
    int n,m,s,t;//結點數,邊數(包括反向弧),源點編號,匯點編號
    vector<Edge>edges;//邊表,dges[e]和dges[e^1]互為反向弧
    vector<int>G[N];//鄰接表,G[i][j]表示結點i的第j條邊在e陣列中的編號
    bool vis[N]; //BFS的使用
    int d[N]; //從起點到i的距離
    int cur[N]; //當前弧下標

    void addedge(int from,int to,double cap)
    {
        edges.push_back(Edge(from,to,cap,0));
        edges.push_back(Edge(to,from,0,0));
        int  m=edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    bool bfs()
    {
        memset(vis,0,sizeof(vis));
        queue<int>Q;
        Q.push(s);
        d[s]=0;
        vis[s]=1;
        while(!Q.empty())
        {
            int x=Q.front();Q.pop();
            for(int i=0;i<G[x].size();i++)
            {
                Edge&e=edges[G[x][i]];
                if(!vis[e.to]&&e.cap>e.flow)//只考慮殘量網路中的弧
                {
                    vis[e.to]=1;
                    d[e.to]=d[x]+1;
                    Q.push(e.to);
                }
            }

        }
        return vis[t];
    }

    double dfs(int x,double a)//x表示當前結點,a表示目前為止的最小殘量
    {
        if(x==t||a==0)return a;//a等於0時及時退出,此時相當於斷路了
        double flow=0,f;
        for(int&i=cur[x];i<G[x].size();i++)//從上次考慮的弧開始,注意要使用引用,同時修改cur[x]
        {
            Edge&e=edges[G[x][i]];//e是一條邊
            if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0)
            {
                e.flow+=f;
                edges[G[x][i]^1].flow-=f;
                flow+=f;
                a-=f;
                if(!a)break;//a等於0及時退出,當a!=0,說明當前節點還存在另一個曾廣路分支。

            }
        }
        return flow;
    }

    double Maxflow(int s,int t)//主過程
    {
        this->s=s,this->t=t;
        double flow=0;
        while(bfs())//不停地用bfs構造分層網路,然後用dfs沿著阻塞流增廣
        {
            memset(cur,0,sizeof(cur));
            flow+=dfs(s,INF);
        }
        return flow;
    }
  };

  int main()
  {
      //freopen("in.txt","r",stdin);
      int t,n,m,L,a,b;
      double w;
      scanf("%d",&t);
      while(t--)
      {
          Dinic dinic;
          scanf("%d%d%d",&n,&m,&L);
          for(int i=1;i<=n;i++)
          {
              scanf("%lf",&w);
              dinic.addedge(0,i,log(w));
          }
          for(int i=1;i<=m;i++)
          {
              scanf("%lf",&w);
              dinic.addedge(n+i,n+m+1,log(w));
          }
          for(int i=0;i<L;i++)
          {
              scanf("%d%d",&a,&b);
              dinic.addedge(a,n+b,INF);
          }
          printf("%.4lf\n",exp(dinic.Maxflow(0,n+m+1)));
      }

      return 0;
  }