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