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 ; }