Destroying The Graph(poj 2125)
题意:
给你一张有向图,你可以选择一个点:
• 摧毁其所有的入边,代价A[i].
• 摧毁其所有的出边,代价B[i].
• 求摧毁这张图的最小代价。
• 数据范围1000
/* 很经典的一道题目(我这么弱,稍微一变形就不会了) 因为每个点涉及到出边和入边,所以可以考虑拆点,然后建图,接下来就成了一个最小点权覆盖的问题。 最小点权覆盖就是求最小割(证明可参考胡伯涛论文“最小割模型在信息学竞赛中的应用”)。 接下来是输出方案,因为我们要选择的点与S或T连得边是满流的,所以可以dfs一边,只走不满流的, 那么如果一个<=n的点走不到,说明它被选择了(这个很好理解),如果一个>n的点能走到,说明它被选择了, 这是因为如果这个点没有被选择,说明从前面水流流过来的时候到某个位置已经割断了,在这里就没必要再割了。 (貌似好难理解的样子,我这么弱肯定想不出来)。 */ #include<cstdio> #include<iostream> #define N 210 #define M 5010 #define inf 1000000000 using namespace std; int a[N],b[N],head[N],dis[N],q[N],vis[N],n,m,cnt=1,S,T; struct node{ int v,f,pre; };node e[M*2]; void add(int u,int v,int f){ e[++cnt].v=v;e[cnt].f=f;e[cnt].pre=head[u];head[u]=cnt; e[++cnt].v=u;e[cnt].f=0;e[cnt].pre=head[v];head[v]=cnt; } bool bfs(){ for(int i=1;i<=T;i++)dis[i]=inf; int h=0,t=1;q[1]=S;dis[S]=0; while(h<t){ int u=q[++h]; for(int i=head[u];i;i=e[i].pre){ int v=e[i].v; if(e[i].f&&dis[u]+1<dis[v]){ dis[v]=dis[u]+1; if(v==T)return true; q[++t]=v; } } } if(dis[T]==inf)return false; return true; } int dinic(int now,int f){ if(now==T)return f; int rest=f; for(int i=head[now];i;i=e[i].pre){ int v=e[i].v; if(e[i].f&&dis[v]==dis[now]+1){ int t=dinic(v,min(rest,e[i].f)); if(!t)dis[v]=0; e[i].f-=t; e[i^1].f+=t; rest-=t; } } return f-rest; } void dfs(int x){ vis[x]=1; for(int i=head[x];i;i=e[i].pre){ if(!e[i].f||vis[e[i].v])continue; dfs(e[i].v); } } int main(){ scanf("%d%d",&n,&m); S=0,T=2*n+1; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); add(i+n,T,a[i]); } for(int i=1;i<=n;i++){ scanf("%d",&b[i]); add(S,i,b[i]); } for(int i=1;i<=m;i++){ int x,y;scanf("%d%d",&x,&y); add(x,y+n,inf); } int min_cnt=0,p=0,pin=0,pout=0; while(bfs()) min_cnt+=dinic(S,inf); printf("%d\n",min_cnt); dfs(S); for(int i=1;i<=n;i++){ if(!vis[i])p++; if(vis[i+n])p++; } printf("%d\n",p); for(int i=1;i<=n;i++){ if(!vis[i])printf("%d -\n",i); if(vis[i+n])printf("%d +\n",i); } return 0; }