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