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
输出样例#1: 复制
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
",ans);
92     return 0;
93 }
原文地址:https://www.cnblogs.com/bztMinamoto/p/9510896.html