poj 3308(3041) 二部图最小点权覆盖(杀死棋盘上所有的伞兵)

题意: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;
}


你可能感兴趣的:(poj 3308(3041) 二部图最小点权覆盖(杀死棋盘上所有的伞兵))