poj 3308 最大割 最小割

Paratroopers

Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 131072/65536K (Java/Other)
Total Submission(s) : 6   Accepted Submission(s) : 0
Problem 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 × 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
 


题意:

       



             火星人侵略地球,他们意图登陆破坏某个地区的兵器工厂。据探子回报,火星人登陆的地区为n*m大小的地域,而且每一个火星人的着陆点坐标已知。

火星人很强悍,只要有一个火星人着陆后能够幸存,他必定能毁坏这片区域的全部兵工厂。为了防止这种情况发生,必须保证在火星人着陆的一瞬间把他们全部同时杀死。

现在防卫队有一个激光枪,开一枪就能把 在同一行(或同一列)着陆的火星人全部杀死。但是这种激光枪的使用是有代价的,把这种激光枪安装到不同行的行首、或者不同列的列首,费用都不同。现在已知把激光枪安装到任意位置的费用,总的花费为这些安装了激光枪的行列花费的乘积。

问怎样安装激光枪才能在杀死所有火星人的前提下费用最少?

解法:

            

  构造方法按照上述把“顶点覆盖问题转化为最小割问题”的方法去处理:

显然取行坐标为二分图的X集合,编号为1~N,点权就是激光炮在第i行射一炮的费用ri;列坐标为二分图的Y集合,编号为N+1~N+M,点权就是激光炮在第j列射一炮的费用cj。

/*
题目意思为:
         有一个n*m的矩阵,告诉了在每一行或者每一列安装大炮的代价,
每一个大炮可以瞬间消灭这一行或者这一列的所有敌人,然后告诉了敌人可能出现的L个坐标位置,
问如何安置大炮,使花费最小。如果一个敌人位于第r行c列,则他可以被第r行或者第c列的大炮消灭。
建立二分图:
            行列对应顶点,敌人对应边,左边的n个顶点对应n行,
右边的m个顶点对应m列,添加源点s,汇点t,当第r行c列出现敌人时,
添加边E(r,c)并使其权值置为无穷大,添加s到左边n个顶点的权值,分别为每行的r1,r2,…rn,
添加右边节点到t的权值,分别为c1,c2,..cn.问题转化为最小点权覆盖问题,即要消灭所有的敌人,
需要覆盖所有的边,并且使选取的这些点的权值最小。
但是放置这些枪也需要一定的费用,这些费用已经给出来了,最后总费用是这些枪的费用之积,现在要求最小的这个费用。
看到积之后,我们可以转换为加法,就是取log。
*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
const int N=4002;
const int M=30002;
const double INF=1e9;
int t,n,m,l,tot;
int gap[M],dis[M],pre[M],head[N],cur[N];
int NE,NV,sink,source;
struct Node
{
    double c;
    int pos,next;
} E[M*4];
#define FF(i,NV) for(int i=0;i<NV;i++)
double sap(int s,int t)
{
    memset(dis,0,sizeof(int)*(NV+1));
    memset(gap,0,sizeof(int)*(NV+1));
    FF(i,NV) cur[i] = head[i];
    int u = pre[s] = s;
    double maxflow = 0,aug =INF;
    gap[0] = NV;
    while(dis[s] < NV)
    {
loop:
        for(int &i = cur[u]; i != -1; i = E[i].next)
        {
            int v = E[i].pos;
            if(E[i].c>0 && dis[u] == dis[v] + 1)
            {
                aug=min(aug,E[i].c);
                pre[v] = u;
                u = v;
                if(v == t)
                {
                    maxflow += aug;
                    for(u = pre[u]; v != s; v = u,u = pre[u])
                    {
                        E[cur[u]].c -= aug;
                        E[cur[u]^1].c += aug;
                    }
                    aug =INF*1.0;
                }
                goto loop;
            }
        }
        if( (--gap[dis[u]]) == 0)   break;
        int mindis = NV;
        for(int i = head[u]; i != -1 ; i = E[i].next)
        {
            int v = E[i].pos;
            if(E[i].c>0 && mindis > dis[v])
            {
                cur[u] = i;
                mindis = dis[v];
            }
        }
        gap[ dis[u] = mindis+1 ] ++;
        u = pre[u];
    }
    return maxflow;
}
void addEdge(int u,int v,double c )
{
    E[NE].c = c;
    E[NE].pos = v;
    E[NE].next = head[u];
    head[u] = NE++;
    E[NE].c = 0;
    E[NE].pos = u;
    E[NE].next = head[v];
    head[v] = NE++;
}
int main()
{
    double r,c;
    scanf("%d",&t);
    int x,y,i,j;
    while(t--)
    {
        NE=0;
        memset(head,-1,sizeof(head));
        scanf("%d%d%d",&m,&n,&l);
        source=0,sink=m+n+1;
        NV=sink+1;
        for(i=1;i<=m;i++)
        {
            scanf("%lf",&r);
            addEdge(source,i,log(r));
        }
        for(j=1;j<=n;j++)
        {
            scanf("%lf",&c);
            addEdge(j+m,sink,log(c));
        }
        for(i=1;i<=l;i++)
        {
            scanf("%d%d",&x,&y);
            addEdge(x,y+m,INF);
        }
        printf("%.4lf\n",exp(sap(source,sink)));
    }
}
/*
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
*/


然后建立超级源S,编号为0,超级汇T,编号为N+M+1。S向X集合每个点都连一条正向弧,边容量为第i点的点权;Y集合每个点都向T连一条正向弧,边容量为第j点的点权。而落有伞兵火星人的区域,表示该位置的x与y是连通的,则从X集合取该点与Y集合的对应点连一条正向弧,边容量为无限大inf。


你可能感兴趣的:(poj 3308 最大割 最小割)