[CQOI2012]交换棋子(最小费用最大流)
Posted hsez-cyx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[CQOI2012]交换棋子(最小费用最大流)相关的知识,希望对你有一定的参考价值。
Description
题目描述
有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,
最终达到目标状态。要求第i行第j列的格子只能参与mi,j次交换。
输入格式
第一行包含两个整数n,m(1<=n, m<=20)。以下n行为初始状态,每行为一个包含m个字符的01串,
其中0表示黑色棋子,1表示白色棋子。以下n行为目标状态,格式同初始状态。
以下n行每行为一个包含m个0~9数字的字符串,表示每个格子参与交换的次数上限。
输出格式
输出仅一行,为最小交换总次数。如果无解,输出-1。
Soltion
先判初始状态和目标状态黑白棋子数是否相等,若不相等直接输出-1
忽略白色棋子,将问题看做把黑色棋子移动到目标位置
容易想到网络流,从 起点 向 每个初始状态为黑色棋子的位置(后文mid) 连一条流量为 1,费用为0 的边
从 每个目标状态为黑色棋子的位置(后文mid) 向 终点 连一条流量为 1 ,费用为0的边
问题在于对交换次数的限制,且一次交换两个位置都算多了一次交换
我们将一个位置的交换分为黑色点交换进去和交换出去两种情况
发现如果某个位置的初始状态和目标状态相同,那这个位置交换进去和交换出去的次数相等
如果不相同,
若初始状态为黑色,则交换出去的次数比交换进去多1;
若目标状态为黑色,则交换进去的次数比交换出去多1;
于是可以想到将一个位置分成3个网络流中的点,分别代表in,mid,out
根据上述分析将交换次数合法分给in-mid边和mid-out边的流量
若位置 i 可与位置 j 交换,则从out_i向in_j连一条流量为inf,费用为1的边
Code
#include <cstdio> #include <cstdlib> #include <queue> #include <algorithm> #include <cstring> using namespace std; const int N=12e2+10,M=1e6; int head[N],nxt[M],ver[M],cost[M],edge[M],tot=1; int s,t,n,m,a[21][21],incf[N],dis[N],maxflow,mincost,pre[N]; char be[21][21],st[21][21],ch[21][21]; int py[8][2]={{1,0},{0,1},{-1,0},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1}}; int id(int x,int y,int inv) { return (x-1)*m+y+inv*n*m; } void add(int u,int v,int w,int c) { ver[++tot]=v,nxt[tot]=head[u],edge[tot]=w,cost[tot]=c,head[u]=tot; ver[++tot]=u,nxt[tot]=head[v],edge[tot]=0,cost[tot]=-c,head[v]=tot; } bool in[N]; bool spfa() { memset(dis,0x3f,sizeof(dis)); memset(pre,0,sizeof(pre)); int last=dis[s]; queue <int> q; dis[s]=0,q.push(s),incf[s]=1<<30; while(!q.empty()) { int x=q.front(); q.pop(); in[x]=false; for(int i=head[x],y;i;i=nxt[i]) if(edge[i]>0 && dis[y=ver[i]]>dis[x]+cost[i]) { dis[y]=dis[x]+cost[i]; incf[y]=min(incf[x],edge[i]); pre[y]=i; if(!in[y]) in[y]=true,q.push(y); } } return dis[t]!=last; } void update() { maxflow+=incf[t],mincost+=incf[t]*dis[t]; int x=t; while(x!=s) { int i=pre[x]; edge[i]-=incf[t],edge[i^1]+=incf[t]; x=ver[i^1]; } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%s",be[i]+1); for(int i=1;i<=n;i++) scanf("%s",st[i]+1); for(int i=1;i<=n;i++) { scanf("%s",ch[i]+1); for(int j=1;j<=m;j++) a[i][j]=ch[i][j]-‘0‘; } s=0,t=n*m*3+1; int ans1=0,ans2=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { for(int k=0;k<8;k++) { int ni=i+py[k][0],nj=j+py[k][1]; if(ni<1 || ni>n || nj<1 || nj>m) continue; add(id(i,j,2),id(ni,nj,0),1<<30,0); } if(be[i][j]==‘0‘) ans1++,add(s,id(i,j,1),1,0); if(st[i][j]==‘0‘) ans2++,add(id(i,j,1),t,1,0); if(a[i][j]==0) continue; if(a[i][j]%2==0 || be[i][j]==st[i][j]) { add(id(i,j,0),id(i,j,1),a[i][j]/2,1); add(id(i,j,1),id(i,j,2),a[i][j]/2,1); continue; } if(be[i][j]==‘0‘) { add(id(i,j,0),id(i,j,1),a[i][j]/2,1); add(id(i,j,1),id(i,j,2),(a[i][j]+1)/2,1); } else { add(id(i,j,0),id(i,j,1),(a[i][j]+1)/2,1); add(id(i,j,1),id(i,j,2),a[i][j]/2,1); } } if(ans1!=ans2) { puts("-1"); return 0; } while(spfa()) update(); if(maxflow<ans2) puts("-1"); else printf("%d ",mincost/2); return 0; }
以上是关于[CQOI2012]交换棋子(最小费用最大流)的主要内容,如果未能解决你的问题,请参考以下文章