BZOJ2668:[CQOI2012]交換棋子(費用流)
題目描述
有一個n行m列的黑白棋盤,你每次可以交換兩個相鄰格子(相鄰是指有公共邊或公共頂點)中的棋子,最終達到目標狀態。要求第i行第j列的格子只能參與mi,j次交換。
輸入輸出格式
輸入格式:
第一行包含兩個整數n,m(1<=n, m<=20)。以下n行為初始狀態,每行為一個包含m個字符的01串,其中0表示黑色棋子,1表示白色棋子。以下n行為目標狀態,格式同初始狀態。以下n行每行為一個包含m個0~9數字的字符串,表示每個格子參與交換的次數上限。
輸出格式:
輸出僅一行,為最小交換總次數。如果無解,輸出-1。
輸入輸出樣例
輸入樣例#1: 復制3 3 110 000 001 000 110 100 222 222 222
4
題解
費用流好題(說的清楚一點就是我根本不會做……)
%%%大佬->這裏
首先考慮,我們可以把交換操作,看作是黑色的點在移動
一個點往下移動的話,當前點和下方的點的交換次數都加一
然後一條移動的路徑,兩端的點移動次數加一,中間的點移動次數加二
如果把這個過程看做網絡流的話,就會有一個問題。因為每一個點開始和結束的顏色可能不同,那麽流入和流出也不同
如果只有一個點,就沒辦法考慮交換次數的限制
而且如果只拆成兩個點的話,就沒辦法考慮路徑兩端的情況了
那麽我們把每一個點拆成三個點$left,now,right$,分別表示只有流入,有流入和流出,只有流出
所以連邊$left->now->right$
然後相互能到達的點之間連邊,容$inf$費$0$
然後考慮如何解決流入流出不平衡
如果開始為白,之後黑,那麽流出必定比流入多一,開始黑,之後白,流入必定比流出多一,顏色一樣,那麽流入流出一樣
然後交換次數最少,在相鄰的格子的邊上加上費用
若該點在初始圖中是黑的、最終圖中是白的,那麽連邊(left,now,1,\frac{limit}{2}),(now,right,1,\frac{limit+1}{2})
若該點在初始圖中是白的、最終圖中是黑的,那麽連邊(left,now,1,\frac{limit+1}{2}
若該點在初始圖和最終圖中顏色相同,那麽連邊(left,now,1,\frac{limit}{2}),(now,right,1,\frac{limit}{2})
這樣就可以保證流量限制了
然後加源,往起始的所有黑點連邊,加匯,讓最終所有黑點往它連邊
然後在能互相到達的點之間連邊
1 //minamoto 2 #include<iostream> 3 #include<cstdio> 4 #include<cstring> 5 #include<queue> 6 #define inf 0x3f3f3f3f 7 #define l(i,j) ((i-1)*m+j) 8 #define now(i,j) (((i-1)*m+j)+n*m) 9 #define r(i,j) (((i-1)*m+j)+(n*m<<1)) 10 using namespace std; 11 const int N=2005,M=50005; 12 int dx[]={1,1,1,0,-1,-1,-1,0},dy[]={1,0,-1,-1,-1,0,1,1}; 13 int ver[M],Next[M],head[N],flow[M],edge[M],tot=1; 14 int dis[N],disf[N],Pre[N],last[N],vis[N]; 15 int n,m,ans=0,S,T; 16 queue<int> q; 17 inline void add(int u,int v,int f,int e){ 18 ver[++tot]=v,Next[tot]=head[u],head[u]=tot,flow[tot]=f,edge[tot]=e; 19 ver[++tot]=u,Next[tot]=head[v],head[v]=tot,flow[tot]=0,edge[tot]=-e; 20 } 21 bool spfa(){ 22 memset(dis,0x3f,sizeof(dis)); 23 q.push(S),dis[S]=0,disf[S]=inf,Pre[T]=-1; 24 while(!q.empty()){ 25 int u=q.front();q.pop(),vis[u]=0; 26 for(int i=head[u];i;i=Next[i]){ 27 int v=ver[i]; 28 if(flow[i]&&dis[v]>dis[u]+edge[i]){ 29 dis[v]=dis[u]+edge[i],Pre[v]=u,last[v]=i; 30 disf[v]=min(disf[u],flow[i]); 31 if(!vis[v]) vis[v]=1,q.push(v); 32 } 33 } 34 } 35 return ~Pre[T]; 36 } 37 int dinic(){ 38 int maxflow=0; 39 while(spfa()){ 40 int u=T;maxflow+=disf[T],ans+=disf[T]*dis[T]; 41 while(u!=S){ 42 flow[last[u]]-=disf[T]; 43 flow[last[u]^1]+=disf[T]; 44 u=Pre[u]; 45 } 46 } 47 return maxflow; 48 } 49 int x1[25][25],x2[25][25],t1,t2; 50 char s[30]; 51 int main(){ 52 scanf("%d%d",&n,&m); 53 S=0,T=n*m*3+1; 54 for(int i=1;i<=n;++i){ 55 scanf("%s",s+1); 56 for(int j=1;j<=m;++j) 57 if(s[j]==‘1‘){ 58 ++t1,add(S,now(i,j),1,0); 59 x1[i][j]=1; 60 } 61 } 62 for(int i=1;i<=n;++i){ 63 scanf("%s",s+1); 64 for(int j=1;j<=m;++j) 65 if(s[j]==‘1‘){ 66 ++t2,add(now(i,j),T,1,0); 67 x2[i][j]=1; 68 } 69 } 70 if(t1!=t2) return puts("-1"),0; 71 for(int i=1;i<=n;++i){ 72 scanf("%s",s+1); 73 for(int j=1;j<=m;++j){ 74 t2=s[j]-‘0‘; 75 if(x1[i][j]==x2[i][j]) 76 add(l(i,j),now(i,j),t2/2,0),add(now(i,j),r(i,j),t2/2,0); 77 else if(x1[i][j]&&!x2[i][j]) 78 add(l(i,j),now(i,j),t2/2,0),add(now(i,j),r(i,j),(t2+1)/2,0); 79 else 80 add(l(i,j),now(i,j),(t2+1)/2,0),add(now(i,j),r(i,j),t2/2,0); 81 } 82 } 83 for(int i=1;i<=n;++i) 84 for(int j=1;j<=m;++j) 85 for(int k=0;k<8;++k){ 86 int ti=i+dx[k],tj=j+dy[k]; 87 if(ti<1||ti>n||tj<1||tj>m) continue; 88 add(r(i,j),l(ti,tj),inf,1); 89 } 90 if(dinic()!=t1) return puts("-1"),0; 91 printf("%d\n",ans); 92 return 0; 93 }
BZOJ2668:[CQOI2012]交換棋子(費用流)