裸的最小割,
拆点之后将求最小割点转化为求最小割边。
题目链接:http://cstest.scu.edu.cn/soj/problem.action?id=3254
题意:
一个连通图中,每个点有自己的权值。
求为了使点0和点n-1不连通所需要去掉的点的权值最小值,点0是不能被去掉的。
算法:
把每个点i拆成两个点,i+1和n+i+1,然后建边(i+n+1,i+1),权值为该点的权值。
注意,城市0是不能被破坏的,所以w(i+1, 1)=INF。
对于原图中的每条边建INF边(u+1,v+n+1)
这样,要使u,v联通的话,则必须有增广路(u+1)->(v+n+1)->(v+1)
所以u,v联通的充要条件是点v不是割点
#include<stdio.h> #include<string.h> #define INF 0x7f7f7f7f int p[2100],d[2100],cur[2100],gap[2100],head[2100]; int E,S,T; typedef struct { int u,v,weigh,next; }EDGE; EDGE edge[1100000]; void addedge(int u,int v,int weigh) { edge[E].v=v; edge[E].u=u; edge[E].weigh=weigh; edge[E].next=head[u]; head[u]=E++; edge[E].v=u; edge[E].u=v; edge[E].weigh=0; edge[E].next=head[v]; head[v]=E++; } int sap(int n) { int v,u,i,a,mind,ans; for(u=1;u<=n;u++) { cur[u]=head[u]; d[u]=gap[u]=0; } gap[0]=n; u=S;ans=0; memset(p,-1,sizeof(p)); while(d[S]<n) { for(i=cur[u];i!=-1;i=edge[i].next) if(edge[i].weigh&&(d[u]==d[edge[i].v]+1)) break; if(i!=-1) { cur[u]=i; int v=edge[i].v; p[v]=i; u=v; if(v==T) { int a=INF; for(;i!=-1;i=p[edge[i].u])a=a<edge[i].weigh?a:edge[i].weigh; for(v=T;v!=S;v=edge[p[v]].u) { edge[p[v]].weigh-=a; edge[p[v]^1].weigh+=a; } ans+=a; u=S; } } else { int mind=n; for(i=head[u];i!=-1;i=edge[i].next) if(edge[i].weigh)if(mind>d[edge[i].v]+1){mind=d[edge[i].v]+1;cur[u]=i;} --gap[d[u]]; if(gap[d[u]]==0)return ans; ++gap[mind]; d[u]= mind; if(u!=S) u=edge[p[u]].u; } } return ans; } int main() { int cas,n,m,i,val,u,v; scanf("%d",&cas); while(cas--) { scanf("%d%d",&n,&m); E=0;S=1;T=n; memset(head,-1,sizeof(head)); addedge(1+n,1,INF); for(i=2;i<=n;i++) { scanf("%d",&val); addedge(i+n,i,val); } while(m--) { scanf("%d%d",&u,&v); addedge(u+1,v+n+1,INF); } printf("%d\n",sap(2*n)); } }
写这篇题解的时候写到一半突然忘了怎么建图了。。
还是看了自己的代码才想起来。。
囧囧囧囧囧。。
看来图论就是要常写才能保持建图的手感。。
不过话说回来,DP还不是要经常写才不会忘记经典模型。。
数据结构还不是要经常写才能处理得好细节。。
杯具啊。。。
就像上次做飞雪的生日邀请赛。。
太久没写线段树了心虚得很,一直以为是自己把模板改错了。
谁知道竟然是数据爆int了导致WA。。。
要是最近写线段树写得勤对自己有信心的话,就不会一遍一遍查自己的代码了,应该就能比较早发现是数据较强的原因。。。哎~~~~~