poj3308--Paratroopers(最小割)

poj3308:题目链接

题目大意:给出一个n*m的矩阵,矩阵的有l个格子会出现外星人,每行的开头和每列的开头都可以装备武器,可以消灭该行或该列的所有外星人,但是每装备一种武器需要有花费,如果装备多种武器,需要的花费是各种花费的乘积。问消灭所有外星人的最小的花费。

输入:给出n m l 然后一行n个数,表示每行武器的花费,之后m个数,是每列的花费,最后是外星人的坐标。

思路:

首先从题意中就可以知道这是一个二分图的覆盖问题,求最小覆盖,但是让求的不是最少的武器数,而是花费数,所以求二分匹配的哪一个就用不上了,只能去用最小割了,两个点集X,Y,X中存行,Y中存列,源点汇点为s,t,s连接到X中,连线的容量为对应行的花费,Y连接到t中,连线的容量为对应的花费,然后外星人的坐标,连接到XY中,容量是INF,这样求出的最大流,也就是最小割的值,也就是最小的花费了

注意:

1、精度,double存15到16位,又因为存在小数,eqs一般为要求的小数位数的两倍,所以eqs = 1e-8,那么double的整数位最多也就是1e8,所以要控制INF的值,不能太大。

2、求的是乘积,将所有容量转化为log(x),log(x*y) = log(x) + log(y),这样就可以用最大流直接求了,最终的结果用exp(x)求回来。

3、poj G++用 %f输出

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
using namespace std ;
#define INF 0x3f3f3f3f
#define eqs 1e-8
struct node{
    int v ;
    double w ;
    int next ;
}edge[10000];
int head[200] , cnt ;
int l[200] ;
queue <int> que ;
void add(int u,int v,double w) {
    edge[cnt].v = v ; edge[cnt].w = w ;
    edge[cnt].next = head[u] ; head[u] = cnt++ ;
}
int bfs(int s,int t) {
    int u , v , i ;
    while( !que.empty() ) que.pop() ;
    memset(l,-1,sizeof(l)) ;
    l[s] = 0 ;
    que.push(s) ;
    while( !que.empty() ) {
        u = que.front() ;
        que.pop() ;
        for(i = head[u] ; i != -1 ; i = edge[i].next) {
            v = edge[i].v ;
            if( l[v] == -1 && edge[i].w >= eqs ) {
                l[v] = l[u] + 1 ;
                que.push(v) ;
            }
        }
    }
    if( l[t] > 0 ) return 1 ;
    return 0 ;
}
double dfs(int s,int t,double min1) {
    if( s == t ) return min1 ;
    int i , v ;
    double ans = 0 , a ;
    for(i = head[s] ; i != -1 ; i = edge[i].next) {
        v = edge[i].v ;
        if( l[v] == l[s]+1 && edge[i].w >= eqs && ( a = dfs(v,t,min(min1,edge[i].w) )  ) ) {
            edge[i].w -= a ;
            edge[i^1].w += a ;
            ans += a ;
            min1 -= a ;
            if( min1 < eqs ) break ;
        }
    }
    if( ans >= eqs ) return ans ;
    l[s] = -1 ;
    return 0 ;
}
int main() {
    int t , n , m , l ;
    int u , v , i , j ;
    double w , max_flow , temp ;
    scanf("%d", &t) ;
    while( t-- ) {
        memset(head,-1,sizeof(head)) ;
        cnt = 0 ;
        scanf("%d %d %d", &n, &m, &l) ;
        for(i = 1 ; i <= n ; i++) {
            scanf("%lf", &w) ;
            add(0,i,log(w)) ;
            add(i,0,0) ;
        }
        for(i = 1 ; i <= m ; i++) {
            scanf("%lf", &w) ;
            add(n+i,n+m+1,log(w)) ;
            add(n+m+i,n+i,0) ;
        }
        while( l-- ) {
            scanf("%d %d", &u, &v) ;
            add(u,n+v,INF) ;
            add(n+v,u,0) ;
        }
        max_flow = 0 ;
        while( bfs(0,n+m+1) ) {
            while( temp = dfs(0,n+m+1,INF) )
                max_flow += temp ;
        }
        printf("%.4lf\n",  exp(max_flow) ) ;
    }
    return 0 ;
}
时间: 2024-11-09 01:44:57

poj3308--Paratroopers(最小割)的相关文章

poj3308 Paratroopers --- 最小点权覆盖-&gt;最小割

题目是一个很明显的二分图带权匹配模型, 添加源点到nx建边,ny到汇点建边,(nx,ny)=inf建边,求最小割既得最小点权覆盖. 在本题中由于求的是乘积,所以先全部取log转换为加法,最后再乘方回来. #include <iostream> #include <cstring> #include <string> #include <cstdio> #include <cmath> #include <algorithm> #inc

zoj 2874 &amp; poj 3308 Paratroopers (最小割)

题意: 一个m*n大小的网格,已知伞兵着陆的具体位置(行和列).现在在某行(或某列) 安装一架激光枪,一架激光枪能杀死该行(或该列)所有的伞兵.在第i行安装一架 激光枪的费用是Ri,在第i列安装的费用是Ci.要安装整个激光枪系统,总费用为这些 激光枪费用的乘积. 求杀死所有伞兵的最小费用. 构图: 把伞兵视为边,行与列视为顶点.增加源点和汇点,对于第i行,从源点向顶点i连接一条 容量为Ri的边.对于第j列,从顶点j向汇点连接一条容量为Rj的边. 如果某一点(i,j)有伞兵降落,则从顶点Ri向顶点

poj3308 Paratroopers --- 最小点权覆盖-&amp;gt;最小割

题目是一个非常明显的二分图带权匹配模型, 加入源点到nx建边,ny到汇点建边,(nx.ny)=inf建边.求最小割既得最小点权覆盖. 在本题中因为求的是乘积,所以先所有取log转换为加法,最后再乘方回来. #include <iostream> #include <cstring> #include <string> #include <cstdio> #include <cmath> #include <algorithm> #in

poj 3308 Paratroopers 最小割 最小点权覆盖

题目链接:http://poj.org/problem?id=3308 题意: 有一个M*N的图,上面的一些点上有伞兵. 可以设置一些枪在每行或者每列上,通过射击,这行或这列的伞兵就会被消灭.每个枪的设置有一个花费,如果设置多个枪,那么花费是设置每个枪的乘积. 问消灭所有伞兵最少的花费是多少. 思路: 每个点的伞兵至少要用那一列或者那一行设置的枪去消灭,那么就可以应用点覆盖的模型.把伞兵看成是一条边,这条边至少要用一个点来覆盖. 而题目中最终花费是所有花费的乘积,那么可以用对数log(x)+lo

POJ3308 Paratroopers(最小割/最小点权覆盖)

把入侵者看作边,每一行每一列都是点,选取某一行某一列都有费用,这样问题就是选总权最小的点集覆盖所有边,就是最小点权覆盖. 此外,题目的总花费是所有费用的乘积,这时有个技巧,就是取对数,把乘法变为加法运算,最后再还原. 另外还可以从最小割的思路去这么理解: 每一行与源点相连,容量为该行的花费:每一列与汇点相连,容量为该列的花费:对于每个入侵者的坐标,该行该列连接一条容量INF的边. 要让源点汇点不连通,割边集必然与所有入侵者的行或列相关,而这样建模后的最小割就是最小的花费(容量INF的边必然不是最

POJ 3308 Paratroopers (二分图最小点权覆盖 -&gt; 最小割 -&gt; 最大流)

POJ 3308 Paratroopers 链接:http://poj.org/problem?id=3308 题意:有一个N*M的方阵,有L个伞兵降落在方阵上.现在要将所有的伞兵都消灭掉,可以在每行每列装一个高射炮,如果在某行(某列)装上高射炮之后,能够消灭所有落在该行(该列)的伞兵.每行每列安高射炮有费用,问如何安装能够使得费用之积最小. 思路:首先题目要求乘积最小,将乘积对e取对数,会发现就变成了求和.然后抽象出一个二分图,每一行是x部的一个点,每个点有权值,权值为费用取ln.每一列是y部

poj3308最小割

这题做的稀里糊涂 首先 建图不会,然后 dinic姿势不对 ,还不知道 为啥....还有这尼玛怎么看出来是 最小割的 #include <cstdio> #include <cstring> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include <cstdlib>

POJ 3308 Paratroopers 最小点权覆盖 求最小割

不懂这个建模是什么原理,以后把二分图相关的东西看完再补上把= = #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include <cstdlib> #i

poj3308 最小割

因为行可以了,那列就不行,所以根据行列建立最小割模型. 然后这题精妙之处在于把乘法取对数后转化为加法,瞬间就简单了. 保证精度,C++AC ,16MS G++WA. #include<stdio.h> #include<string.h> #include<queue> #include<math.h> #define maxn 120 #define INF 10000000 using namespace std; struct node { int t

POJ 3308--Paratroopers【 最小点权覆盖 &amp;&amp; 最小割】

Paratroopers Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7847   Accepted: 2365 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 infor