题意:n*m棋盘上有若干伞兵,现欲在若干行或若干列上建立武器(i行上的武器能干掉i行上所有的伞兵,列同理),给出在每行(列)上的建造费用,已知总费用为所有武器的费用的乘积。求干掉所有士兵的最小费用。(3041也就是3308的简化版,每个武器费用为1,直接求最小点覆盖即可)
思路:行和列分别作为二部图的一部,有伞兵在i行j列上则(i,j)有边。如果这个伞兵被干掉,那么i行或者j列必选一个。也就转化成了求二部图的最小点权覆盖。还是转化为最大流(最小割)来求。因为总费用是所有费用的乘积,所以与源点或者汇点相连的边的权值要取对数,最后取指数为答案。
#include <stdio.h> #include <string.h> #include <math.h> #define min(a,b) ((a)<(b)?(a):(b)) #define INF 0x3fffffff #define N 110 int T,n,m,k; double f[N][N],a[N],q[N]; int p[N]; double maxflow(int s,int t){ int i; double res=0; while(1){ int front,rear,now; front = rear = -1; q[++rear] = s; memset(a,0,sizeof(a)); a[s] = INF; memset(p,0,sizeof(p)); while(front < rear){ now = q[++front]; for(i = s;i<=t;i++) if(!a[i] && min(f[now][i],a[now]) > 0){ a[i] = min(f[now][i],a[now]); p[i] = now; q[++rear] = i; } } if(!a[t]) break; res += a[t]; for(i = t;i!=s;i=p[i]){ f[p[i]][i] -= a[t]; f[i][p[i]] += a[t]; } } return res; } int main(){ scanf("%d",&T); while(T--){ int i,j,w; double a; memset(f,0,sizeof(f)); scanf("%d %d %d",&n,&m,&k); for(i = 1;i<=n;i++){ scanf("%lf",&a); f[0][i] = log(a)/log(2.); } for(i = 1;i<=m;i++){ scanf("%lf",&a); f[n+i][n+m+1] = log(a)/log(2.); } for(i = 0;i<k;i++){ scanf("%d %d",&j,&w); f[j][n+w] = INF; } printf("%.4lf\n",pow(2,maxflow(0,n+m+1))); } return 0; }
3041:
#include <stdio.h> #include <string.h> #define N 505 struct edge{ int y,next; }e[10005]; int lk[N],used[N],n,m,first[N],top; void add(int x,int y){ e[top].y = y; e[top].next = first[x]; first[x] = top++; } int dfs(int x){ int i,y; for(i = first[x];i!=-1;i=e[i].next){ y = e[i].y; if(!used[y]){ used[y] = 1; if(lk[y]==-1 || dfs(lk[y])){ lk[y] = x; return 1; } } } return 0; } int hungary(){ int i,res=0; for(i = 1;i<=n;i++){ memset(used, 0, sizeof(used)); if(dfs(i)) res++; } return res; } int main(){ while (scanf("%d %d",&n,&m)!=EOF) { int i,a,b; memset(first, -1, sizeof(first)); memset(lk, -1, sizeof(lk)); top = 0; for(i = 0;i<m;i++){ scanf("%d %d",&a,&b); add(a,b); } printf("%d\n",hungary()); } return 0; }