题目连接:http://acm.pku.edu.cn/JudgeOnline/problem?id=2125
题目大意:给一个有m条边的有向图,现在要进行如下操作,删除一这个点为终点的边需要花费w2-,删除以这个点为起点的边要花费 w1+;求删除所有的边时要花费的最少代价;
算法:2分图的最小点权覆盖;把每个点分成两个点建成2分图;ISAP算法求网络流。。。具体的解题报告 请见胡伯涛大牛2007集训论文。。求出网络流以后,必存在最小割集和使其之和等于最大网络流。。。那么以source为中点,dfs一次,与之有关联的为一个集合,没关系的就给另外一集合。求之于source与sink相连的中间边即可。。。
#include<stdio.h> #include<string.h> const int N = 500; const int inf = 0x7fffffff; int map[N][N],sign[N][N],w1[N],w2[N]; int n,m,source,sink; int q[N]; int d[N]; int num[N]; int Cur[N]; int pre[N]; int used[N]; int cnt[N][2]; void Rel_BFS() { for(int i=1;i<=n;i++) num[d[i]=n]++; int i,j,head,tail; head=tail=0; q[0]=sink; num[n]--; d[sink]=0; num[0]++; while(head<=tail) { i=q[head++]; for(j=1;j<=n;j++) { if(map[j][i]==0||d[j]<n) continue; num[n]--; d[j]=d[i]+1; num[d[j]]++; q[++tail]=j; } } } int Augment() { int i,Min_flow=inf; for(i=sink;i!=source;i=pre[i]) if(map[pre[i]][i]<Min_flow) Min_flow=map[pre[i]][i]; for(i=sink;i!=source;i=pre[i]) map[pre[i]][i]-=Min_flow,map[i][pre[i]]+=Min_flow; return Min_flow; } int Retreat(int &i) { int tmp; int j,mind=n-1; for(j=1;j<=n;j++) if(map[i][j]>0&&d[j]<mind) mind=d[j]; tmp=d[i]; num[d[i]]--; d[i]=mind+1; num[d[i]]++; if(i!=source) i=pre[i]; return num[tmp]; } int find_max_flow() { int flow=0,i,j; Rel_BFS(); for(int i=1;i<=n;i++) Cur[i]=1; i=source; while(d[source]<n) { for(j=1;j<=n;j++) if(map[i][j]>0&&d[i]==d[j]+1) break; if(j<=n) { Cur[i]=j; pre[j]=i; i=j; if(i==sink) { flow+=Augment(); i=source; } } else { Cur[i]=1; if(Retreat(i)==0) break; } } return flow; } void dfs(int u) { if(used[u]) return ; used[u]=1; for(int i=1;i<=n;i++) if(i!=u&&!used[i]&&map[u][i]>0) dfs(i); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",w1+i); for(int i=1;i<=n;i++) scanf("%d",w2+i); int a,b; memset(map,0,sizeof(map)); memset(sign,0,sizeof(sign)); for(int i=0;i<m;i++) { scanf("%d%d",&a,&b); map[a][b+n]=inf; sign[a][b+n]=1; map[2*n+1][a]=w2[a]; sign[2*n+1][a]=1; map[b+n][2*n+2]=w1[b]; sign[b+n][2*n+2]=1; } n=n*2+2; source=n-1,sink=n; int ans=find_max_flow(); printf("%d/n",ans); memset(used,0,sizeof(used)); dfs(source); ans=0; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(used[i]&&!used[j]&&sign[i][j]) { cnt[ans][0]=i,cnt[ans][1]=j; ans++; } printf("%d/n",ans); for(int i=0;i<ans;i++) { if(cnt[i][0]==source) printf("%d -/n",cnt[i][1]); else if(cnt[i][1]==sink) printf("%d +/n",cnt[i][0]-(n-2)/2); } return 0; }