BZOJ1001 [BeiJing2006]狼抓兔子(網路流最小割)
阿新 • • 發佈:2019-01-28
題目可以轉化為:從原圖中選出一個邊集,使得去掉它之後,(1,1)與(n,m)不通
即:以(1,1)為源,(n,m)為匯,求該圖最小割
不過由於節點過多,直接對輸入的圖求最小割的話會超時
轉化:求它的對偶圖,然後求最短路,見圖,紅字為點編號,綠線為建的邊(邊權為其經過的原圖的邊的權值)
注意:1.計算出轉化後點數、邊數的範圍(易錯!)
即:以(1,1)為源,(n,m)為匯,求該圖最小割
不過由於節點過多,直接對輸入的圖求最小割的話會超時
轉化:求它的對偶圖,然後求最短路,見圖,紅字為點編號,綠線為建的邊(邊權為其經過的原圖的邊的權值)
注意:1.計算出轉化後點數、邊數的範圍(易錯!)
2.n==1 或 m==1 的特殊情況
程式碼:
#include<stdio.h> #include<stdlib.h> #define INF 1000000000 int u[6000010]={0},v[6000010]={0},w[6000010]={0},first[2000005]={0},next[6000010]={0},d[2000005]={0},q[3000005]={0},hash[2000005]={0}; int e=0; void tj(int x,int y,int z) { u[++e]=x; v[e]=y; w[e]=z; next[e]=first[x]; first[x]=e; } int main() { int n,m,i,j,s,t,x,y,node,head=0,tail=1,min=INF; scanf("%d%d",&n,&m); s=2*(n-1)*(m-1)+1; node=t=s+1; for(i=1;i<=n;i++) for(j=1;j<m;j++) { scanf("%d",&x); if(min>x) min=x; if(i==1) tj(j,t,x); if(i>1&&i<n) { tj((2*i-2)*(m-1)+j,(2*i-3)*(m-1)+j,x); tj((2*i-3)*(m-1)+j,(2*i-2)*(m-1)+j,x); } if(i==n) tj(s,(2*i-3)*(m-1)+j,x); } for(i=1;i<n;i++) for(j=1;j<=m;j++) { scanf("%d",&x); if(min>x) min=x; if(j==1) tj(s,(2*i-1)*(m-1)+1,x); if(j>1&&j<m) { tj((2*i-2)*(m-1)+j-1,(2*i-2)*(m-1)+m+j-1,x); tj((2*i-2)*(m-1)+m+j-1,(2*i-2)*(m-1)+j-1,x); } if(j==m) tj((2*i-1)*(m-1),t,x); } for(i=1;i<n;i++) for(j=1;j<m;j++) { scanf("%d",&x); if(min>x) min=x; tj((2*i-1)*(m-1)+j,(2*i-2)*(m-1)+j,x); tj((2*i-2)*(m-1)+j,(2*i-1)*(m-1)+j,x); } if(n==1||m==1) printf("%d",min);//注意特判n或m==1 !!! else { for(i=1;i<=node;i++) d[i]=INF; d[s]=0; q[0]=s; hash[s]=1; while(head!=tail) { for(i=first[q[head]];i!=0;i=next[i]) if(d[v[i]]>d[u[i]]+w[i]) { d[v[i]]=d[u[i]]+w[i]; if(hash[v[i]]==0) { hash[v[i]]=1; q[tail]=v[i]; tail++; if(tail>=2500000) tail-=2500000; } } hash[q[head]]=0; head++; if(head>=2500000) head-=2500000; } printf("%d",d[t]); } return 0; }