【解题思路】
显然,这题的答案是这个网格图的最小割。根据最大流-最小割定理,我们可以用网络流算法来求其最小割,时间复杂度最小为O(V2√E)。
特殊的,这个网格图是一个平面图,于是可以根据平面图最小割-最短路定理,转化为其对偶图的最短路,时间复杂度最小为O(kE)或O(Vlog2V)(民科算法spfa前途不可估量)。
【参考代码】
恩,听说这题我当初RE得生活不能自理,于是直接贴了orz::hzwer大神的代码。。
贴个T掉的SAP板子。。(已修正了假板子的当前弧优化。。但还是T得生活不能自理。。)
1 #pragma GCC optimize(2) 2 #include <algorithm> 3 #include <cstring> 4 #include <functional> 5 #define REP(i,low,high) for(register int i=(low);i<=(high);++i) 6 #define INF (0x7f7f7f7f) 7 #define function(type) __attribute__((optimize("-O2"))) inline type 8 #define procedure __attribute__((optimize("-O2"))) inline void 9 using namespace std; 10 11 //ex_cmp { 12 template<typename T,class Compare> 13 inline bool getcmp(T&target,const T&pattern,Compare comp) 14 { 15 return comp(pattern,target)?target=pattern,1:0; 16 } 17 //} ex_cmp 18 19 //quick_io { 20 #include <cctype> 21 #include <cstdio> 22 function(int) getint() 23 { 24 char c=getchar(); for(;!isdigit(c)&&c!='-';c=getchar()); 25 short s=1; for(;c=='-';c=getchar()) s*=-1; int r=0; 26 for(;isdigit(c);c=getchar()) r=(r<<3)+(r<<1)+c-'0'; 27 return s*r; 28 } 29 //} quick_io 30 31 static const int N=1000000,M=3000000; static int cardE=0; 32 33 struct edge 34 { 35 int fr,to,cap; 36 edge(const int&f=0,const int&t=0,const int&c=0) 37 :fr(f),to(t),cap(c) {} 38 }edg[(M<<1)+5]; 39 40 int hed[N+5],nxt[(M<<1)+5],hsh[1005][1005]; 41 42 procedure add_edge(const int&fr,const int&to,const int&cp) 43 { 44 edg[cardE]=edge(fr,to,cp),nxt[cardE]=hed[fr],hed[fr]=cardE++; 45 } 46 47 //SAP { 48 int aug[N+5],cur[N+5],dis[N+5],gap[N+5],path[N+5]; 49 50 function(int) augment(const int&S,const int&T) 51 { 52 for(register int i=T;i!=S;i=edg[path[i]].fr) 53 { 54 edg[path[i]].cap-=aug[T],edg[path[i]^1].cap+=aug[T]; 55 } 56 return aug[T]; 57 } 58 59 function(int) SAP(const int&S,const int&T,const int&N) 60 { 61 memset(aug,0,sizeof aug),memset(gap,0,sizeof gap); 62 memset(dis,0,sizeof dis); REP(i,1,N) cur[i]=hed[i]; 63 aug[S]=INF,gap[0]=N; int ret=0; 64 for(register int fr=S;dis[S]<N;) 65 { 66 if(fr==T) ret+=augment(fr=S,T); bool flag=1; 67 for(register int i=cur[fr];~i;i=nxt[i]) 68 { 69 int to=edg[i].to; 70 if(edg[i].cap&&dis[fr]==dis[to]+1) 71 { 72 aug[to]=min(aug[fr],edg[i].cap) 73 ,path[to]=cur[fr]=i,fr=to,flag=0; break; 74 } 75 } 76 if(flag) 77 { 78 if(!--gap[dis[fr]]) break; dis[fr]=N; 79 for(register int i=hed[fr];~i;i=nxt[i]) 80 if(edg[i].cap) 81 { 82 getcmp(dis[fr],dis[edg[i].to]+1,less<int>()); 83 } 84 ++gap[dis[fr]],cur[fr]=hed[fr]; 85 if(fr!=S) fr=edg[path[fr]].fr; 86 } 87 } 88 return ret; 89 } 90 //} SAP 91 92 int main() 93 { 94 int n=getint(),m=getint(),cardP=0; 95 REP(i,1,n) REP(j,1,m) hsh[i][j]=++cardP; 96 memset(hed,-1,sizeof hed),memset(nxt,-1,sizeof nxt); 97 REP(i,1,n) REP(j,2,m) 98 { 99 int w=getint(); 100 add_edge(hsh[i][j-1],hsh[i][j],w), 101 add_edge(hsh[i][j],hsh[i][j-1],w); 102 } 103 REP(i,2,n) REP(j,1,m) 104 { 105 int w=getint(); 106 add_edge(hsh[i-1][j],hsh[i][j],w), 107 add_edge(hsh[i][j],hsh[i-1][j],w); 108 } 109 REP(i,2,n) REP(j,2,m) 110 { 111 int w=getint(); 112 add_edge(hsh[i-1][j-1],hsh[i][j],w), 113 add_edge(hsh[i][j],hsh[i-1][j-1],w); 114 } 115 printf("%d\n",SAP(1,cardP,cardP)); 116 return 0; 117 }
然后对偶图最短路。。(被队列长度卡了好久。。用了循环队列才过。。)
1 #pragma GCC optimize(2) 2 #include <algorithm> 3 #include <cstring> 4 #include <functional> 5 #define REP(i,low,high) for(register int i=(low);i<=(high);++i) 6 #define INF (0x3f3f3f3f) 7 #define function(type) __attribute__((optimize("-O2"))) inline type 8 #define procedure __attribute__((optimize("-O2"))) inline void 9 using namespace std; 10 11 //ex_cmp { 12 template<typename T,class Compare> 13 inline bool getcmp(T&target,const T&pattern,Compare comp) 14 { 15 return comp(pattern,target)?target=pattern,1:0; 16 } 17 //} ex_cmp 18 19 //quick_io { 20 #include <cctype> 21 #include <cstdio> 22 function(long long) getint() 23 { 24 char c=getchar(); for(;!isdigit(c)&&c!='+'&&c!='-';c=getchar()); 25 short s=1; for(;c=='+'||c=='-';c=getchar()) if(c=='-') s*=-1; 26 long long r=0; for(;isdigit(c);c=getchar()) r=(r<<3)+(r<<1)+c-'0'; 27 return s*r; 28 } 29 //} quick_io 30 31 static const int N=2000000,M=3000000,SIZE=(N<<1)+10; 32 33 struct edge 34 { 35 int to,cap; edge(const int&t=0,const int&c=0):to(t),cap(c) {} 36 }edg[(M<<1)+10]; 37 38 static int cardE=0; int hed[N+10],nxt[(M<<1)+10]; 39 40 procedure add_edge(const int&fr,const int&to,const int&cp) 41 { 42 edg[++cardE]=edge(to,cp),nxt[cardE]=hed[fr],hed[fr]=cardE; 43 } 44 45 //SPFA { 46 bool inq[N+10]={0}; int dis[N+10]={0},q[SIZE]; 47 48 function(int&) move(int&n) {return ++n==SIZE?n=0:n;} 49 50 function(int) SPFA(const int&S,const int&T) 51 { 52 memset(dis,0x3f,sizeof dis),inq[q[dis[S]=0]=S]=1; 53 for(register int head=-1,tail=0;head!=tail;) 54 { 55 int fr=q[move(head)]; 56 for(register int i=hed[fr];i;i=nxt[i]) 57 { 58 int to=edg[i].to; 59 if( 60 getcmp(dis[to],dis[fr]+edg[i].cap,less<int>()) 61 &&!inq[to] 62 ) inq[q[move(tail)]=to]=1; 63 } 64 inq[fr]=0; 65 } 66 return dis[T]; 67 } 68 //} SPFA 69 70 int main() 71 { 72 int n=getint(),m=getint(),nm=(n-1)*(m-1)<<1; 73 if(n==1||m==1) 74 { 75 int ans=INF; 76 REP(i,1,max(m,n)-1) getcmp(ans,(int)getint(),less<int>()); 77 return printf("%d\n",ans),0; 78 } 79 REP(i,1,m-1) 80 { 81 int w=getint(); add_edge(i,nm+1,w),add_edge(nm+1,i,w); 82 } 83 REP(i,1,n-2) REP(j,1,m-1) 84 { 85 int w=getint(),fr=(i<<1)*(m-1)+j,to=fr-m+1; 86 add_edge(fr,to,w),add_edge(to,fr,w); 87 } 88 REP(i,1,m-1) 89 { 90 int w=getint(),tmp=((n<<1)-3)*(m-1)+i; 91 add_edge(0,tmp,w),add_edge(tmp,0,w); 92 } 93 REP(i,0,n-2) 94 { 95 int w=getint(),tmp=(i<<1)*(m-1)+m; 96 add_edge(0,tmp,w),add_edge(tmp,0,w); 97 REP(j,2,m-1) 98 { 99 int fr=(i<<1)*(m-1)+j-1,to=fr+m; 100 add_edge(fr,to,w=getint()),add_edge(to,fr,w); 101 } 102 tmp=(m-1)*(i<<1|1),w=getint(), 103 add_edge(tmp,nm+1,w),add_edge(nm+1,tmp,w); 104 } 105 REP(i,0,n-2) REP(j,1,m-1) 106 { 107 int w=getint(),fr=(i<<1)*(m-1)+j,to=fr+m-1; 108 add_edge(fr,to,w),add_edge(to,fr,w); 109 } 110 printf("%d\n",SPFA(0,nm+1)); 111 return 0; 112 }
We Secure, We Contain, We Protect.