/************************************************************** Problem: 1001 User: whymhe Language: C++ Result: Accepted Time:1236 ms Memory:106760 kb ****************************************************************/ //Pro:P4001 [BJOI2006]狼抓兔子 //大概就是求个最小割 //这个题的唯一的难点大概就是如何给网格图编号 //一个比较坑的地方: //边都是双向的,所以正反向弧的容量都是flow #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; inline int read() { char c=getchar();int num=0; for(;!isdigit(c);c=getchar()); for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num; } const int N=1e6+5; int n,m,S,T; int head[N],num_edge; struct Edge { int v,flow,nxt; }edge[N<<3]; inline void add_edge(int u,int v,int flow) { edge[++num_edge].v=v; edge[num_edge].flow=flow; edge[num_edge].nxt=head[u]; head[u]=num_edge; } int from[N],dep[N]; inline bool bfs() { queue<int> que; int now; for(now=S;now<=T;++now) dep[now]=0,from[now]=head[now]; que.push(S),dep[S]=1; while(!que.empty()) { now=que.front(),que.pop(); for(int i=head[now],v;i;i=edge[i].nxt) { v=edge[i].v; if(dep[v]||!edge[i].flow) continue; dep[v]=dep[now]+1; if(v==T) return 1; que.push(v); } } return 0; } int dfs(int u,int flow) { if(u==T||!flow) return flow; int outflow=0,tmp; for(int &i=from[u],v;i;i=edge[i].nxt) { v=edge[i].v; if(dep[v]!=dep[u]+1) continue; tmp=dfs(v,min(flow,edge[i].flow)); if(!tmp) continue; flow-=tmp; outflow+=tmp; edge[i].flow-=tmp; edge[i^1].flow+=tmp; if(!flow) return outflow; } dep[u]=0; return outflow; } int main() { num_edge=1; n=read(),m=read(); S=1,T=n*m; for(int i=1,p,f;i<=n;++i) { for(int j=1;j<m;++j) { p=(i-1)*m+j; f=read(); add_edge(p,p+1,f); add_edge(p+1,p,f); } } for(int i=1,p,f;i<n;++i) { for(int j=1;j<=m;++j) { p=(i-1)*m+j; f=read(), add_edge(p,p+m,f); add_edge(p+m,p,f); } } for(int i=1,p,f;i<n;++i) { for(int j=1;j<m;++j) { p=(i-1)*m+j; f=read(); add_edge(p,p+m+1,f); add_edge(p+m+1,p,f); } } long long ans=0; while(bfs()) ans+=1ll*dfs(S,0x7fffffff); printf("%lld",ans); return 0; }