题解 P3159 【[CQOI2012]交换棋子】
George1123 · · 题解
P3159 【[CQOI2012]交换棋子】
此题算法:费用流
题目很简洁,做法很恶心的典型。
因为是网络流题,所以模板就不说了,只考虑加边。
大致思路:
简化问题
记录初始和结束状态,把白棋看作没棋。
把开始结束都有黑棋的格子看作没棋。
如果开始结束时黑棋数不等,
加边
1.拆点,每个格子有格子
控制格子交换次数。
2.
表示需匹配状态。
3.每个黑棋格
表示匹配状态。
4.每个格子
两次交换只会消耗
1 的流量。
※.如果格子初始或结束时有黑棋并且允许交换数为奇数,在上面那条边上附上
不交换本来就要通过的流量。
5.每个格子
用来交换。
然后跑模板就好了,网络流的题都差不多。
图片仅供参考,以实物为准。
以下是代码 + 注释
#include <bits/stdc++.h>
using namespace std;
const int N=1e3+10;
const int M=5e4+10;
const int inf=1e8+10;
int p,n,m,s,t,S,T,fans,cans;
struct edge{
int adj,nex,fw,r;
}e[M];
int g[N],top=1;
void add(int x,int y,int z,int w){
e[++top]=(edge){y,g[x],z,w};
g[x]=top;
}
void Add(int x,int y,int z,int w){
add(x,y,z,w),add(y,x,0,-w);
}
int dep[N],cur[N];
bool vis[N];
queue<int> Q;
bool spfa(){
// puts("spfa()");
for(int i=1;i<=p;i++)
vis[i]=0,dep[i]=inf,cur[i]=g[i];
Q.push(s),vis[s]=1,dep[s]=0;
while(Q.size()){
int x=Q.front(); Q.pop();
vis[x]=0;
for(int i=g[x];i;i=e[i].nex){
int to=e[i].adj,d=e[i].r;
if(e[i].fw&&dep[to]>dep[x]+d){
dep[to]=dep[x]+d;
if(!vis[to]){
vis[to]=1;
Q.push(to);
}
}
}
}
return dep[t]!=inf;
}
int dfs(int x,int F){
// puts("dfs");
if(!F||x==t)
return F;
int flow=0,f;
vis[x]=1;
for(int i=cur[x];i;i=e[i].nex){
int to=e[i].adj; cur[x]=i;
if(!vis[to]&&dep[x]+e[i].r==dep[to]&&
(f=dfs(to,min(F,e[i].fw)))>0){
e[i].fw-=f;
e[i^1].fw+=f;
flow+=f,F-=f;
if(!F){
vis[x]=0;
break;
}
}
}
return flow;
}
int P(int x,int y){return (x-1)*m+y;} //点序
int tx[]={-1,1,0,0,-1,-1,1,1};
int ty[]={0,0,-1,1,1,-1,1,-1}; //八向
int Ss[25][25],Ts[25][25]; //初始,终局
int main(){
scanf("%d%d",&n,&m);
p=t=2*n*m+2,s=t-1;
char c[25];
for(int i=1;i<=n;i++){
scanf("%s",c);
for(int j=1;j<=m;j++)
if(c[j-1]=='1')
Ss[i][j]=1,S++;
}
for(int i=1;i<=n;i++){
scanf("%s",c);
for(int j=1;j<=m;j++)
if(c[j-1]=='1')
Ts[i][j]=1,T++;
}
if(S!=T) return puts("-1"),0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(Ss[i][j]&&!Ts[i][j])
Add(s,P(i,j),1,0),fans++; //2
if(!Ss[i][j]&&Ts[i][j])
Add(P(i,j)+n*m,t,1,0); //3
}
}
for(int i=1,x;i<=n;i++){
scanf("%s",c);
for(int j=1;j<=m;j++){
x=c[j-1]-'0';
Add(P(i,j),P(i,j)+n*m,x>>1,0); //4
if((Ss[i][j]^Ts[i][j])&&(x&1))
Add(P(i,j),P(i,j)+n*m,1,0); //※
for(int k=0;k<8;k++){
int xt=i+tx[k],yt=j+ty[k];
if(xt<1||xt>n||yt<1||yt>m)
continue;
Add(P(i,j)+n*m,P(xt,yt),inf,1); //5
}
}
}
while(spfa()){
int d=dfs(s,inf);
fans-=d;
cans+=d*dep[t];
}
if(fans) puts("-1");
else printf("%d\n",cans);
return 0;
}
图是手画的,写题解不易。
关注博主,为文章点赞是你应该做的。
谢谢大家! !