poj 2125 二部图最小点权覆盖

题意:N个点M条边的有向图,给出如下两种操作。删除点I的所有出边,代价是AI。删除点J的所有入边,代价是BI。求最后删除图中所有的边的最小代价。

思路:如果不考虑权值,就是一个二分图的最小点覆盖问题,加入权值后,变成了二分图的最小点权覆盖问题。二分图的最小点权覆盖问题可以转化为网络流模型来求解。具体方法为:将每个点拆成两个,意为将两个操作视为两个点v1,v2.源点与v1的弧容量为删除出度的耗费,v2与汇点的弧容量为删除入度的耗费,v1若与v2有联系,则连一条容量为无穷的边。建图完成后求最大流,然后dfs求割边

#include <stdio.h>
#include <string.h>
#define INF 0x3fffffff
#define min(a,b) ((a)<(b)?(a):(b))
#define N 210
#define M 5010
int n,m,p[N],used[N],c[N][N],out[N],top;
int maxflow(int s,int t){
	int q[200000],front,rear,i,now,res=0;
	while(1){
		front = rear = -1;
		q[++rear] = s;
		memset(p,0,sizeof(p));
		memset(used,0,sizeof(used));
		used[s] = INF;
		while(front < rear){
			now = q[++front];
			for(i = s;i<=t;i++)
				if(!used[i] && min(used[now],c[now][i])>0){
					q[++rear] = i;
					used[i] = min(used[now],c[now][i]);
					p[i] = now;
				}
		}
		if(!used[t])
			break;
		res += used[t];
		for(i = t;i!=s;i=p[i]){
			c[p[i]][i] -= used[t];
			c[i][p[i]] += used[t];
		}
	}
	return res;
}
void dfs(int i){
	int j;
	used[i] = 1;
	for(j = 0;j<=2*n+1;j++)
		if(!used[j] && c[i][j]>0)
			dfs(j);
}
int main(){
	freopen("a.txt","r",stdin);
	while(scanf("%d %d",&n,&m)!=EOF){
		int i,a,b;
		memset(c,0,sizeof(c));
		top = 0;
		for(i = 1;i<=n;i++){
			scanf("%d",&a);
			c[n+i][2*n+1] = a;
		}
		for(i = 1;i<=n;i++){
			scanf("%d",&a);
			c[0][i] = a;
		}
		for(i = 1;i<=m;i++){
			scanf("%d %d",&a,&b);
			c[a][b+n] = INF;
		}
		printf("%d\n",maxflow(0,2*n+1));
		memset(used,0,sizeof(used));
		dfs(0);
		for(i = 1;i<=n;i++){
			if(!used[i])
				out[top++] = i;
			if(used[i+n])
				out[top++] = i+n;
		}
		printf("%d\n",top);
		for(i = 0;i<top;i++){
			if(out[i] > n)
				printf("%d +\n",out[i]-n);
			else
				printf("%d -\n",out[i]);
		}
	}
	return 0;
}


你可能感兴趣的:(poj 2125 二部图最小点权覆盖)