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。
输入输出样例
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}),(now,right,1,\frac{limit}{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 }