Description
给出一张网格,左上角点为(1,1),右下角点为(N,M).有以下三种类型的道路
1:(x,y)<==>(x+1,y)
2:(x,y)<==>(x,y+1)
3:(x,y)<==>(x+1,y+1)
截断一条边的代价为它的权值,求最小的代价使得从左上角不可到达右下角。
Solution
很明显最小割就好了。
写个dinic练练手。(好久没写网络流了)
注意这里的边是双向边。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define rep(i,a) for(int i=last[a];i;i=next[i])
#define N 1000005
using namespace std;
const int inf=0x7fffffff;
int n,m,x,l,S,T,ans,last[N],dis[N],d[N],next[N*6],v[N*6],t[N*6];
void add(int x,int y,int z) {
t[++l]=y;v[l]=z;next[l]=last[x];last[x]=l;
t[++l]=x;v[l]=z;next[l]=last[y];last[y]=l;
}
int get(int x,int y) {return (x-1)*m+y;}
bool bfs() {
memset(dis,0,sizeof(dis));dis[S]=1;
int i=0,j=1;d[1]=S;
while (i<j) rep(k,d[++i])
if (!dis[t[k]]&&v[k])
dis[t[k]]=dis[d[i]]+1,d[++j]=t[k];
return dis[T];
}
int dinic(int x,int y) {
if (x==T) return y;
int now=0;
rep(i,x) if (dis[t[i]]==dis[x]+1&&v[i]) {
int k=dinic(t[i],min(y,v[i]));
v[i]-=k;v[i^1]+=k;y-=k;now+=k;
if (!y) break;
}
if (!now) dis[x]=-1;
return now;
}
int main() {
scanf("%d%d",&n,&m);S=1;T=n*m;l=1;
fo(i,1,n) fo(j,1,m-1) scanf("%d",&x),add(get(i,j),get(i,j+1),x);
fo(i,1,n-1) fo(j,1,m) scanf("%d",&x),add(get(i,j),get(i+1,j),x);
fo(i,1,n-1) fo(j,1,m-1) scanf("%d",&x),add(get(i,j),get(i+1,j+1),x);
while (bfs()) ans+=dinic(S,inf);
printf("%d",ans);
}