题目描述
有一个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
好像所有的没有思路的题都能用毒瘤的网络流来搞一下,
这题真的毒瘤,第一次见拆三个点的
看到一个点最多参与几次交换,就应该想到拆点限流,然后最后问你最先的代价,应该可以联想到费用这一块,
那就向费用流这块搞啊,这题的建模是真的难想,所以说就不详细写了,细节挺多的。
然后就是不用担心一个1到达目标的时候,打乱的别的1,因为这时通过交换两个点的先后顺序来避免这个问题。
放个网址: https://www.luogu.org/problemnew/solution/P3159
1 #include<queue>
2 #include<cstdio>
3 #include<cstring>
4 #include<iostream>
5 #include<algorithm>
6 #define MAXN 2010
7 #define MAXM 64010
8 #define INF 0x3f3f3f3f
9 #define fpop(x) x.front();x.pop()
10 using namespace std;
11
12 int pre_node[MAXN],pre_edge[MAXN];
13
14 char ch,mp_bg[25][25],mp_ed[25][25];
15
16 int n,m,cnt=-1,dis[MAXN],vis[MAXN],flow[MAXN],head[MAXN],maxf[25][25];
17
18 struct edge{
19 int nxt,to,w,f;
20 }e[MAXM];
21
22 inline int _bg(int x,int y){return n*m*0+(x-1)*m+y;}//起始点[x,y]的编号
23 inline int _ed(int x,int y){return n*m*1+(x-1)*m+y;}//目标点[x,y]的编号
24 inline int _inn(int x,int y){return n*m*2+(x-1)*m+y;}//棋盘[x,y]的Inn点编号
25 inline int _mid(int x,int y){return n*m*3+(x-1)*m+y;}//棋盘[x,y]的mid点标号
26 inline int _out(int x,int y){return n*m*4+(x-1)*m+y;}//棋盘[x,y]的Out点编号
27
28 inline bool in_map(int x,int y){
29 return 1<=x && x<=n && 1<=y && y<=m;
30 }//判断是否越界
31
32 inline void add_edge(int from,int to,int flw,int val){
33 e[++cnt].nxt=head[from];
34 e[cnt].to=to;
35 e[cnt].f=flw;
36 e[cnt].w=val;
37 head[from]=cnt;
38 }
39
40 queue<int>que;
41
42 inline bool spfa(int s,int t){
43 memset(vis,0,sizeof(vis));
44 memset(dis,0x3f,sizeof(dis));
45 memset(flow,0x3f,sizeof(flow));
46 que.push(s); vis[s]=true; dis[s]=0;
47 while(!que.empty()){
48 int u=fpop(que);
49 for(int i=head[u];~i;i=e[i].nxt){
50 int v=e[i].to;
51 if(dis[v]>dis[u]+e[i].w && e[i].f){
52 dis[v]=dis[u]+e[i].w;
53 flow[v]=min(flow[u],e[i].f);
54 pre_node[v]=u;
55 pre_edge[v]=i;
56 vis[v]=true;que.push(v);
57 }
58 }
59 }
60 return dis[t]!=INF;
61 }
62
63 int mv[8][2]={{1,0},{-1,0},{0,1},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1}};
64
65 int main(){
66 memset(head,-1,sizeof(head));
67 scanf("%d%d",&n,&m);
68 for(int i=1;i<=n;++i){
69 for(int j=1;j<=m;++j){
70 scanf(" %c",&mp_bg[i][j]);
71 }
72 }
73 for(int i=1;i<=n;++i){
74 for(int j=1;j<=m;++j){
75 scanf(" %c",&mp_ed[i][j]);
76 }
77 }
78 for(int i=1;i<=n;++i){
79 for(int j=1;j<=m;++j){
80 scanf(" %c",&ch);
81 maxf[i][j]=ch-'0';
82 //最大经过次数
83 }
84 }
85 //输入起始态和目标态棋盘
86 int s=0,t=n*m*5+1;
87 int cnt_1=0,cnt_2=0;
88 for(int i=1;i<=n;++i){
89 for(int j=1;j<=m;++j){
90 if(mp_bg[i][j]==mp_ed[i][j]){
91 add_edge(_inn(i,j),_mid(i,j),maxf[i][j]/2,0);
92 add_edge(_mid(i,j),_inn(i,j),000000000000,0);
93
94 add_edge(_mid(i,j),_out(i,j),maxf[i][j]/2,0);
95 add_edge(_out(i,j),_mid(i,j),000000000000,0);
96 }else{
97 if(mp_bg[i][j]=='1'){
98 add_edge(_inn(i,j),_mid(i,j),(maxf[i][j]+0)/2,0);
99 add_edge(_mid(i,j),_inn(i,j),000000000000,0);
100
101 add_edge(_mid(i,j),_out(i,j),(maxf[i][j]+1)/2,0);
102 add_edge(_out(i,j),_mid(i,j),000000000000,0);
103 }
104 if(mp_ed[i][j]=='1'){
105 add_edge(_inn(i,j),_mid(i,j),(maxf[i][j]+1)/2,0);
106 add_edge(_mid(i,j),_inn(i,j),000000000000,0);
107
108 add_edge(_mid(i,j),_out(i,j),(maxf[i][j]+0)/2,0);
109 add_edge(_out(i,j),_mid(i,j),000000000000,0);
110 }
111 }
112
113 if(mp_bg[i][j]=='1'){
114 ++cnt_1;
115 //连接源点到初始点 f=1 w=0;
116 add_edge(s,_mid(i,j),1,0);
117 add_edge(_mid(i,j),s,0,0);
118 //连接起始点到棋盘
119 // add_edge(_bg(i,j),_mid(i,j),1,0);
120 // add_edge(_mid(i,j),_bg(i,j),0,0);
121 }
122 if(mp_ed[i][j]=='1'){
123 ++cnt_2;
124 //连接终结点到汇点 f=1 w=0;
125 add_edge(_mid(i,j),t,1,0);
126 add_edge(t,_mid(i,j),0,0);
127 //连接棋盘到终结点
128 // add_edge(_mid(i,j),_ed(i,j),1,0);
129 // add_edge(_ed(i,j),_mid(i,j),0,0);
130 }
131 //棋盘的八连通边 f=INF w=1;
132 for(int k=0;k<8;++k){
133 int ni=i+mv[k][0];
134 int nj=j+mv[k][1];
135 if(in_map(ni,nj)){
136 //从点[i,j]的out连接点[ni,nj]的inn
137 add_edge(_out(i,j),_inn(ni,nj),INF,+1);
138 add_edge(_inn(ni,nj),_out(i,j),000,-1);
139 }
140 }
141 }
142 }
143 //棋子数变动->No solution
144 if(cnt_1!=cnt_2){
145 puts("-1");
146 return 0;
147 }
148 //然后跑费用流
149 int max_flow=0,min_cost=0;
150 while(spfa(s,t)){
151 max_flow+=flow[t];
152 min_cost+=flow[t]*dis[t];
153 int u=t;
154 while(u!=s){
155 e[pre_edge[u]^0].f-=flow[t];
156 e[pre_edge[u]^1].f+=flow[t];
157 u=pre_node[u];
158 }
159 }
160 if(max_flow!=cnt_1){
161 puts("-1");
162 return 0;
163 }
164 printf("%d
",min_cost);
165 }