题目:BZOJ1001、洛谷P4001。
题目大意:在一张n×m的网格图中,每个格子都与其右、下、右下方各连有一条带权无向边。现在要你割去一些边,使得左上角的点无法到达右下角的点。并且要割掉的边的总权值最小。问最小是多少。
解题思路:题意是求最小割,根据最小割等于最大流的定理,转化为最大流即可。
由于无向图,建反向边时容量和正向边一样。
但本题裸的dinic是过不了的,需要加一个优化:如果沿一条边走,发现返回值为0,则说明这条边已经无任何贡献了,在本次增广中不需要再考虑这条边(代码第54行)。
然后优化一下常数就可以卡过去了。
C++ Code:
#include<stdio.h> #include<cctype> #include<cstring> #define N 1000002 #define INF 0x3f3f3f3f int n,m,level[N],iter[N],head[N],cnt=0; struct edge{ int to,cap,rev,nxt; }e[N*7]; int q[5000005]; inline int min(int a,int b){return a<b?a:b;} inline int readint(){ char c=getchar(); int p=0; for(;!isdigit(c);c=getchar()); for(;isdigit(c);c=getchar()) p=p*10+c-‘0‘; return p; } inline void addedge(int from,int to,int cap){ ++cnt; e[cnt]=(edge){to,cap,cnt+1,head[from]}; head[from]=cnt; ++cnt; e[cnt]=(edge){from,cap,cnt-1,head[to]}; head[to]=cnt; } void bfs(int s){ memset(level,-1,sizeof(level)); level[s]=0; int l=0,r=1; q[1]=s; while(l!=r){ int u=q[l=l%5000000+1]; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(level[v]<0&&e[i].cap>0){ level[v]=level[u]+1; q[r=r%5000000+1]=v; } } } } int dfs(int u,int t,int f){ if(u==t)return f; for(int& i=iter[u];i;i=e[i].nxt){ int v=e[i].to; if(e[i].cap>0&&level[v]>level[u]){ int d=dfs(v,t,min(f,e[i].cap)); if(d){ e[i].cap-=d; e[e[i].rev].cap+=d; return d; }else level[v]=-1; } } return 0; } int max_flow(int s,int t){ int flow=0; while(1){ bfs(s); if(level[t]<0)return flow; memcpy(iter,head,sizeof(iter)); int f; while(f=dfs(s,t,INF))flow+=f; } } int main(){ n=readint(),m=readint(); for(int i=1;i<=n;++i) for(int j=1;j<m;++j){ int t=readint(); addedge((i-1)*m+j,(i-1)*m+j+1,t); } for(int i=1;i<n;++i) for(int j=1;j<=m;++j){ int t=readint(); addedge((i-1)*m+j,i*m+j,t); } for(int i=1;i<n;++i) for(int j=1;j<m;++j){ int t=readint(); addedge((i-1)*m+j,i*m+j+1,t); } printf("%d\n",max_flow(1,n*m)); return 0; }