Asa's Chess Problem

一、题目

给定一张 \(n\times n\) 的矩阵,每个点上面有黑棋或者是白棋,给定 \(\frac{n\times n}{2}\) 对可以交换的位置,每对位置一定在同一行 \(/\) 同一列。\(R[i],C[j]\) 分别表示 \(i\)\(j\) 列的黑棋数,要求交换后 \(Rl[i]\leq R[i]\leq Rr[i],Cl[i]\leq C[i]\leq Cr[i]\)

问最小交换次数,如果无解输出 \(-1\)

二、解法

还是比较有意思的,因为这道题的限制在行列上,所以我们把行列拿出来建图。

最好把交换的过程在网络上表示出来,流量代表黑棋,一开始的初始状态有的黑棋就先 把流量给对应的行和列 。然后考虑表示这个限制,连一条上下界为限制的边到汇点 ,问题转化成带上下界的网络流。

最后来表示交换过程,同色的棋子可以忽略。一定要注意每对位置一定是同行或者同列,如果同行,那么把有黑棋的列连向无黑棋的列,如果同列,那么把有黑棋的行连向无黑棋的行。题目的这个条件保证了行和列是相对独立的,所以在我们的建图中并没有行和列的边(这也是一个思维定式,要敢于跳出来)

最后总结一下我们的建图,拿着这个图去跑最小费用可行流就可以了(无特殊说明费用为 \(0\)):

  • \(s\) 连行,上下界都是初始黑棋数,行连 \(t\) ,上下界是题目中的要求。
  • \(s\) 连列,上下界都是初始黑棋数,列连 \(t\) ,上下界是题目中的要求。
  • \((x_1,y_1),(x_2,y_2)\) ,设 \((x_1,y_1)\) 初始有黑棋且不同色,如果 \(x_1=x_2\)\(y_1\)\(y_2\) 一条容量为 \(1\) ,费用为 \(1\) 的边,如果 \(y_1=y_2\)\(x_1\)\(x_2\) 一条容量为 \(1\) ,费用为 \(1\) 的边。

由于评测机挂了,我的代码只保证能通过样例,但方法是对的,请相信我。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int M = 205;
const int inf = 0x3f3f3f3f;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,s,t,s1,t1,tot,sum,cost,f[M],in[M],out[M],a[55][55];
int ins[M],dis[M],flow[M],lst[M],pre[M],r[M],c[M];
struct edge
{
	int v,f,c,next;
	edge(int V=0,int F=0,int C=0,int N=0) :
	v(V) , f(F) , c(C) , next(N) {}
}e[M*M];
void add(int u,int v,int c,int fl)
{
	e[++tot]=edge(v,fl,c,f[u]),f[u]=tot;
	e[++tot]=edge(u,0,-c,f[v]),f[v]=tot;
}
int xez(int u,int v,int a,int b,int c)
{
	in[v]+=a;out[u]+=a;
	add(u,v,c,b-a);
}
int bfs() 
{
	queue<int> q;
	for(int i=0;i<=t;i++) dis[i]=inf;
	flow[s]=inf;dis[s]=0;pre[s]=-1;
	q.push(s);
	while(!q.empty())
	{
		int u=q.front();q.pop();
		ins[u]=0;
		for(int i=f[u];i;i=e[i].next)
		{
			int v=e[i].v,c=e[i].c;
			if(dis[v]>dis[u]+c && e[i].f>0)
			{
				dis[v]=dis[u]+c;
				flow[v]=min(flow[u],e[i].f);
				pre[v]=u;lst[v]=i;
				if(!ins[v])
				{
					ins[v]=1;
					q.push(v);
				}
			}
		}
	}
	return dis[t]<inf;
}
void solve()
{
	s1=0;t1=2*n+1;s=2*n+2;t=2*n+3;
	sum=cost=0;tot=1;
	for(int i=0;i<=t;i++) f[i]=in[i]=out[i]=0;
	for(int i=1;i<=n;i++) r[i]=c[i]=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			a[i][j]=read();
			r[i]+=a[i][j];
			c[j]+=a[i][j];
		}
	for(int i=1;i<=n;i++)
	{
		int a=read(),b=read();
		xez(s1,i,r[i],r[i],0);
		xez(i,t1,a,b,0);
	}
	for(int i=1;i<=n;i++)
	{
		int a=read(),b=read();
		xez(s1,i+n,c[i],c[i],0);
		xez(i+n,t1,a,b,0);
	}
	for(int i=1;i<=n*n/2;i++)
	{
		int u=read(),v=read(),p=read(),q=read();
		if(a[u][v]==a[p][q]) continue;
		if(a[u][v]==0) swap(u,p),swap(v,q);
		if(u==p) add(v+n,q+n,1,1);//同行 
		else add(u,p,1,1);//同列 
	}
	for(int i=0;i<=t1;i++)
	{
		if(in[i]>out[i]) add(s,i,0,in[i]-out[i]),sum+=in[i]-out[i];
		else add(i,t,0,out[i]-in[i]);
	}
	add(t1,s1,0,inf);
	while(bfs())
	{
		sum-=flow[t];
		cost+=flow[t]*dis[t];
		int zy=t;
		while(zy!=s)
		{
			e[lst[zy]].f-=flow[t];
			e[lst[zy]^1].f+=flow[t];
			zy=pre[zy];
		}
	}
	if(sum>0) puts("-1");
	else printf("%d\n",cost);
}
int main()
{
	while(~scanf("%d",&n)) solve();
}
posted @ 2021-02-06 11:46  C202044zxy  阅读(91)  评论(0编辑  收藏  举报