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