P3159 [CQOI2012]交换棋子 题解

Description

传送门

Solution

Part 1: 简化版

首先,考虑这样一个简化版的问题: 第 i i i 行第 j j j 列的点被经过不超过 l i m i , j lim_{i,j} limi,j 次时,求最少移动步数。

首先,我们把白棋看做没棋。特别的,若 a i , j = b i , j = 1 a_{i,j}=b_{i,j}=1 ai,j=bi,j=1(即对应位均为黑棋),那么我们也将其均看做没棋。

考虑网络流建模。为满足点的限制,将每一个 ( i , j ) (i,j) (i,j) 拆成入点 L i , j L_{i,j} Li,j 和出点 R i , j R_{i,j} Ri,j

  • ∀ ( i , j ) \forall (i,j) (i,j),若 a i , j = 1 a_{i,j}=1 ai,j=1,连边 ( s , L i , j , 1 , 0 ) (s,L_{i,j},1,0) (s,Li,j,1,0)
  • ∀ ( i , j ) \forall (i,j) (i,j),若 b i , j = 1 b_{i,j}=1 bi,j=1,连边 ( R i , j , t , 1 , 0 ) (R_{i,j},t,1,0) (Ri,j,t,1,0)
  • ∀ ( i , j ) \forall (i,j) (i,j),连边 ( L i , j , R i , j , l i m i , j , 0 ) (L_{i,j},R_{i,j},lim_{i,j},0) (Li,j,Ri,j,limi,j,0)
  • ∀ ( x 0 , y 0 ) \forall (x_0,y_0) (x0,y0),对于所有与其相邻的 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),连边 ( R x 0 , y 0 , L x 1 , y 1 , ∞ , 1 ) (R_{x_0,y_0},L_{x_1,y_1},∞,1) (Rx0,y0,Lx1,y1,,1)

跑最小费用最大流即可。

Part 2: 原题

回到原题。

此时 l i m i , j lim_{i,j} limi,j 作为了交换次数的上界,这意味着什么呢?不难发现,对于某条形如 a → b → c → d a \to b \to c \to d abcd 的棋子移动路径, b , c b,c b,c 会额外参与两次交换 a , d a,d a,d 只会参与一次交换

因此,我们需要把 L i , j L_{i,j} Li,j R i , j R_{i,j} Ri,j 之间的边的流量修改为 ⌊ l i m i , j 2 ⌋ \lfloor \frac {lim_{i,j}} {2} \rfloor 2limi,j。特别的,若 a i , j a_{i,j} ai,j b i , j b_{i,j} bi,j 有二者之一为 0 0 0(注意在 Part 1 中我们已经把 a i , j = b i , j = 1 a_{i,j}=b_{i,j}=1 ai,j=bi,j=1 的情况给处理掉了),那么我们再附上一条流量为 1 1 1 的边。

时间复杂度 O ( M C M F ( n 2 , n 2 ) ) O(MCMF(n^2,n^2)) O(MCMF(n2,n2)),可以通过本题。

Code

#include <bits/stdc++.h>
#define int long long
#define inf 20000000000007
using namespace std;
const int maxn=1005,maxp=3005;

int read(){
    
    
	int s=0,w=1;char ch=getchar();
	while (ch<'0'||ch>'9'){
    
    if (ch=='-')  w=-w;ch=getchar();}
	while (ch>='0'&&ch<='9'){
    
    s=(s<<1)+(s<<3)+(ch^'0');ch=getchar();}
	return s*w;
}
int n,m,s,t,sum,cnt=1;
int head[maxp],a[maxn][maxn],b[maxn][maxn],lim[maxn][maxn];

struct edge{
    
    int nxt,to,f,d;}e[2000*maxp];

int dx[8]={
    
    -1,-1,-1, 0, 0, 1, 1, 1};
int dy[8]={
    
    -1, 0, 1,-1, 1,-1, 0, 1};

void add_edge(int u,int v,int f,int d){
    
    
	cnt++;
	e[cnt].to=v,e[cnt].f=f,e[cnt].d=d;
	e[cnt].nxt=head[u],head[u]=cnt;
}
void Add(int u,int v,int f,int d){
    
    
//	cout<<"ADD:"<<u<<' '<<v<<' '<<f<<' '<<d<<endl;
	add_edge(u,v,f,d),add_edge(v,u,0,-d);
}

namespace MCMF{
    
    
	int dis[maxp],flow[maxp],pre[maxp],last[maxp],vis[maxp];
	bool SPFA(){
    
    
		for (int i=s;i<=t;i++)  dis[i]=flow[i]=inf,vis[i]=0;
		
		queue<int> q;
		q.push(s),dis[s]=0,vis[s]=1;
		while (!q.empty()){
    
    
			int now=q.front();
			q.pop();vis[now]=0;
			
			for (int i=head[now];i;i=e[i].nxt){
    
    
				int y=e[i].to;
				if (dis[y]>dis[now]+e[i].d&&e[i].f>0){
    
    
					dis[y]=dis[now]+e[i].d;
					flow[y]=min(flow[now],e[i].f);
					pre[y]=now,last[y]=i;
					if (!vis[y])  q.push(y),vis[y]=1;
				}
			}
		}
		if (dis[t]>=inf)  return false;
		else return true;
	}
	int calc(){
    
    
		int mincost=0,maxflow=0;
		while (SPFA()){
    
    
			int now=t;
			maxflow+=flow[t];
			mincost+=flow[t]*dis[t];
			while (now!=s){
    
    
				e[last[now]].f-=flow[t];
				e[last[now]^1].f+=flow[t];
				now=pre[now];
			}
		}
		return mincost;
	}
}
namespace ducati{
    
    
	int pos(int x,int y,int d){
    
    return d*n*m+(x-1)*m+y;}
	void get_all_in(){
    
    
		n=read(),m=read();
		for (int i=1;i<=n;i++){
    
    
			for (int j=1;j<=m;j++){
    
    
				char x;cin>>x;
				a[i][j]=x-'0',sum-=a[i][j];
			}
		}
		for (int i=1;i<=n;i++){
    
    
			for (int j=1;j<=m;j++){
    
    
				char x;cin>>x;
				b[i][j]=x-'0',sum+=b[i][j];
			}
		}
		for (int i=1;i<=n;i++){
    
    
			for (int j=1;j<=m;j++){
    
    
				char x;cin>>x;
				lim[i][j]=x-'0';
			}
		}
	}
	void build(){
    
    
		s=0,t=2*n*m+1;
		for (int i=1;i<=n;i++){
    
    
			for (int j=1;j<=m;j++){
    
    
				if (a[i][j]&b[i][j])  a[i][j]=b[i][j]=0;
				
				int x=pos(i,j,0),y=pos(i,j,1);
				if (a[i][j])  Add(s,x,1,0);
				if (b[i][j])  Add(y,t,1,0);
				Add(x,y,lim[i][j]/2,0);
				if (a[i][j]|b[i][j]){
    
    
					if (lim[i][j]&1)  Add(x,y,1,0);
				}
				for (int k=0;k<8;k++){
    
    
					int ii=i+dx[k],jj=j+dy[k];
					if (ii>=1&&ii<=n&&jj>=1&&jj<=m)
					  Add(y,pos(ii,jj,0),inf,1);
				}
			}
		}
	}
	void solve(){
    
    
		get_all_in(),build();
		if (sum==0)  cout<<MCMF::calc()<<endl;
		else cout<<-1<<endl;
	}
}

signed main(){
    
    
	ducati::solve();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Cherrt/article/details/120803257
今日推荐