1. 程式人生 > >BZOJ 3774 最優選擇 (最小割+二分圖)

BZOJ 3774 最優選擇 (最小割+二分圖)

www. bre turn get using 最大 集合 head www

題面傳送門

題目大意:給你一個網格圖,每個格子都有$a_{ij}$的代價和$b_{ij}$的回報,對於格子$ij$,想獲得$b_{ij}$的回報,要麽付出$a_{ij}$的代價,要麽$ij$周圍四聯通的格子都付出代價,求最大的回報-代價

好神的一道題,%%%jr

想獲得$b_{ij}$的回報,要麽付出$a_{ij}$的代價,要麽$ij$周圍四聯通的格子都付出代價

所以把棋盤像國際象棋一樣黑白交叉染色,原圖就變成了一個類似於二分圖的東西

每個格子都拆成$2$個點

對於白格子,源點$S$向$W1$連流量為$a_{ij}$的邊,$W1$向$W2$連流量為$b_{ij}$的邊,$W2$向白格子周圍四個黑格子的$B2$連流量為$inf$的邊

對於黑格子,$B2$向匯點$T$連流量為$a_{ij}$的邊,$B1$向$B2$連流量為$b_{ij}$的邊,周圍四個白格子的$W1$向$B1$連流量為$inf$的邊

然後跑最大流,答案就是總回報-最大流

為什麽要這麽建邊?我們可以對割進行分析

如果一個白格子的代價邊$a_{ij}$被割掉了,說明這個格子帶來的回報$\geq$代價,被歸到了$T$集合裏,此時流量$=$代價,統計答案時會加上回報$-$代價

如果一個白格子的代價邊$a_{ij}$沒被割掉,說明這個格子帶來的回報$<$代價,被歸到了$S$集合裏,此時流量$=$回報,統計答案時會把這部分回報去掉

黑格子也是同理

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #define N1 5010
  5 #define M1 30010
  6 #define L1 55
  7 using namespace std;
  8 const int inf=0x3f3f3f3f;
  9 
 10 int gint()
 11 {
 12     int ret=0,fh=1;char c=getchar();
 13     while(c<0||c>
9){if(c==-)fh=-1;c=getchar();} 14 while(c>=0&&c<=9){ret=ret*10+c-0;c=getchar();} 15 return ret*fh; 16 } 17 struct Edge{ 18 int to[M1<<1],nxt[M1<<1],flow[M1<<1],head[N1],cte; 19 void ae(int u,int v,int f) 20 { 21 cte++; to[cte]=v; nxt[cte]=head[u]; 22 head[u]=cte; flow[cte]=f; 23 } 24 }e; 25 26 int dep[N1],que[M1],cur[N1],n,m,hd,tl,S,T; 27 int bfs() 28 { 29 int x,j,v; 30 memset(dep,-1,sizeof(dep)); memcpy(cur,e.head,sizeof(cur)); 31 hd=1,tl=0; que[++tl]=S; dep[S]=0; 32 while(hd<=tl) 33 { 34 x=que[hd++]; 35 for(j=e.head[x];j;j=e.nxt[j]) 36 { 37 v=e.to[j]; 38 if( dep[v]==-1 && e.flow[j]>0 ) 39 { 40 dep[v]=dep[x]+1; 41 que[++tl]=v; 42 } 43 } 44 } 45 return dep[T]!=-1; 46 } 47 int dfs(int x,int limit) 48 { 49 int j,v,flow,ans=0; 50 if(!limit||x==T) return limit; 51 for(j=cur[x];j;j=e.nxt[j]) 52 { 53 v=e.to[j]; cur[x]=j; 54 if( dep[v]==dep[x]+1 && (flow=dfs(v,min(limit,e.flow[j]))) ) 55 { 56 e.flow[j]-=flow; limit-=flow; 57 e.flow[j^1]+=flow; ans+=flow; 58 if(!limit) break; 59 } 60 } 61 return ans; 62 } 63 int Dinic() 64 { 65 int mxflow=0,j,v,ans=0; 66 while(bfs()) 67 mxflow+=dfs(S,inf); 68 return mxflow; 69 } 70 71 int xx[4]={-1,0,1,0},yy[4]={0,1,0,-1}; 72 int a[L1][L1],b[L1][L1],id[L1][L1]; 73 inline int check(int x,int y){return (x<1||y<1||x>n||y>m)?0:1;} 74 75 int main() 76 { 77 scanf("%d%d",&n,&m); 78 int i,j,x,y,k,tot=n*m,sum=0; e.cte=1; S=0; T=tot+tot+1; 79 for(i=1;i<=n;i++) for(j=1;j<=m;j++) a[i][j]=gint();// sum+=v[i][j]; 80 for(i=1;i<=n;i++) for(j=1;j<=m;j++) b[i][j]=gint(), id[i][j]=(i-1)*m+j, sum+=b[i][j]; 81 for(i=1;i<=n;i++) for(j=1;j<=m;j++) 82 { 83 x=id[i][j]; 84 if((i+j)&1){ 85 e.ae(S,x,a[i][j]); e.ae(x,S,0); 86 e.ae(x,x+tot,b[i][j]); e.ae(x+tot,x,0); 87 for(k=0;k<4;k++) 88 { 89 if(!check(i+xx[k],j+yy[k])) continue; 90 y=id[i+xx[k]][j+yy[k]]; 91 e.ae(x+tot,y+tot,inf); e.ae(y+tot,x+tot,0); 92 } 93 }else{ 94 e.ae(x+tot,T,a[i][j]); e.ae(T,x+tot,0); 95 e.ae(x,x+tot,b[i][j]); e.ae(x+tot,x,0); 96 for(k=0;k<4;k++) 97 { 98 if(!check(i+xx[k],j+yy[k])) continue; 99 y=id[i+xx[k]][j+yy[k]]; 100 e.ae(y,x,inf); e.ae(x,y,0); 101 } 102 } 103 } 104 printf("%d\n",sum-Dinic()); 105 return 0; 106 }

BZOJ 3774 最優選擇 (最小割+二分圖)