2668: [cqoi2012]交换棋子
Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 1334 Solved: 518
[Submit][Status][Discuss]
Description
有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态。要求第i行第j列的格子只能参与mi,j次交换。
Input
第一行包含两个整数n,m(1<=n, m<=20)。以下n行为初始状态,每行为一个包含m个字符的01串,其中0表示黑色棋子,1表示白色棋子。以下n行为目标状态,格式同初始状态。以下n行每行为一个包含m个0~9数字的字符串,表示每个格子参与交换的次数上限。
Output
输出仅一行,为最小交换总次数。如果无解,输出-1。
Sample Input
3 3
110
000
001
000
110
100
222
222
222
110
000
001
000
110
100
222
222
222
Sample Output
4
HINT
Source
费用流,建图神建图。。。
照例说建图,看上去貌似毫无头绪,然而实际上还是可做的,首先可以看到有一个步数上限,自然就往网络流那边想,然后求最小交换的步数,应该貌似看上去是求一个最小费用,所以费用流。
首先自然是要拆点,比较特殊的是,这个题一个点拆三个点。。。。我们分别叫他们x,y,z,首先先从源点向所有是一的y点连一条容量为1,免费的边,从y点向汇点也这么连。然后对于这两张图,如果第一张图中有棋子,那么就连v[i][j]/2从x到y,(v[i][j]+1)/2从y到z,如果只是第二张图里有,那么就换换加一。最后把所有能互相影响的点从z到x连无限流0费的边,一遍费用流即可。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #define ll long long #define inf 500000000 #define re register #define id ((i-1)*m+j) using namespace std; struct po { int from,to,dis,nxt,w; }edge[2500001]; int head[500001],dep[60001],n,m,s,t,u,num=-1,x,y,l,tot,sum,k,fa[10001]; int dis[50005],b[50005],xb[50001],flow[50001]; char map1[501][501],map2[501][51],mapn[501][510],cnt; int dx[9]={0,-1,-1,-1,0,1,1,1,0}; int dy[9]={0,-1,0,1,1,1,0,-1,-1}; inline long long read() { long long x=0,c=1; char ch=' '; while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); while(ch=='-')c*=-1,ch=getchar(); while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar(); return x*c; } inline void add_edge(int from,int to,int w,int dis) { edge[++num].nxt=head[from]; edge[num].to=to; edge[num].w=w; edge[num].dis=dis; head[from]=num; } inline void add(int from,int to,int w,int dis) { add_edge(from,to,w,dis); add_edge(to,from,0,-dis); } inline bool spfa() { memset(b,0,sizeof(b)); for(re int i=0;i<=t;i++) dis[i]=inf; deque<int> q; b[t]=1;dis[t]=0; q.push_back(t); while(!q.empty()) { int u=q.front(); q.pop_front(); b[u]=0; for(re int i=head[u];i!=-1;i=edge[i].nxt) { int v=edge[i].to; if(edge[i^1].w>0&&dis[v]>dis[u]-edge[i].dis) { dis[v]=dis[u]-edge[i].dis; if(!b[v]) { b[v]=1; if(!q.empty()&&dis[v]>dis[q.front()]) q.push_front(v); else q.push_back(v); } } } } return dis[s]<inf; } inline int dfs(int u,int low) { if(u==t) { b[t]=1; return low; } int diss=0; b[u]=1; for(re int i=head[u];i!=-1;i=edge[i].nxt) { int v=edge[i].to; if(!b[v]&&edge[i].w!=0&&dis[v]==dis[u]-edge[i].dis) { int check=dfs(v,min(low,edge[i].w)); if(check>0) { tot+=check*edge[i].dis; low-=check; diss+=check; edge[i].w-=check; edge[i^1].w+=check; if(low==0) break; } } } return diss; } inline int max_flow() { int ans=0; while(spfa()) { b[t]=1; while(b[t]) { memset(b,0,sizeof(b)); ans+=dfs(s,inf); } } return ans; } int main() { memset(head,-1,sizeof(head)); n=read();m=read(); s=0;t=n*m*3+1; int N=n*m; for(re int i=1;i<=n;i++) for(re int j=1;j<=m;j++){ cin>>map1[i][j]; if(map1[i][j]=='1') add(s,id+N,1,0),cnt++; } for(re int i=1;i<=n;i++) for(re int j=1;j<=m;j++){ cin>>map2[i][j]; if(map2[i][j]=='1') add(id+N,t,1,0); } for(re int i=1;i<=n;i++) for(re int j=1;j<=m;j++) cin>>mapn[i][j]; for(re int i=1;i<=n;i++) for(re int j=1;j<=m;j++){ if(map1[i][j]==map2[i][j]=='1'){ add(id,id+N,(mapn[i][j]-'0')/2,1);add(id+N,id+N+N,(mapn[i][j]-'0')/2,0); }else if(map1[i][j]=='1'){ add(id,id+N,(mapn[i][j]-'0')/2,1);add(id+N,id+N+N,(mapn[i][j]+1-'0')/2,0); }else if(map2[i][j]=='1'){ add(id,id+N,(mapn[i][j]+1-'0')/2,1);add(id+N,id+N+N,(mapn[i][j]-'0')/2,0); }else add(id,id+N,(mapn[i][j]-'0')/2,1),add(id+N,id+N+N,(mapn[i][j]-'0')/2,0); for(re int k=1;k<=8;k++){ int lx=dx[k]+i; int ly=dy[k]+j; if(lx>=1&&lx<=n&&ly>=1&&ly<=m){ add(id+N+N,(lx-1)*m+ly,inf,0); } } } sum=max_flow(); if(sum>=cnt) cout<<tot; else cout<<"-1"; }