[bzoj2668]-[CQOI2012]交换棋子

发布于 2018-01-21  113 次阅读



题目(bzoj)
题目(洛谷)
这题明显是费用流......
建模方法:
将每个格子拆成两个点,分别为“入点”和“出点”,分别表示进入这个格子以及出去这个格子,中间接一条边,费用为0.
然后这个流量嘛......
分成两种情况:
1.当前格子初末状态相同,则流量为m[i][j]/2;
2.当前格子初末状态不同,则流量为(m[i][j]+1)/2。
这个其实很好理解:初末状态相同,那么肯定有一次交换将当前格子交换进来并且不出去;初末状态不相同,那么肯定有一次交换将当前格子交换进来并且交换出去。
然后,每个格子向相邻格子接边,流量为INF,费用为1。
S向每个格子的“入点”接一条边,流量为1,费用为0;每个格子的“出点”向T接一条边,流量为1,费用为0。
最后跑一遍费用流,完毕。
另外注意特判:若最大流+初末状态相同且为白点的格子数!=所有白点格子数,则无解。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <climits>
#include <cctype>
#include <cstdlib>
#define ll long long
#define llmax LONG_LONG_MAX
#define llmin LONG_LONG_MIN
#include <ext/rope>
#include <ext/pb_ds/assoc_container.hpp>
#define maxn 2050
using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
template <class _E> inline void read(_E &e)
{
	e=0;bool ck=0;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')ck=1;ch=getchar();}
	while(ch>='0'&&ch<='9'){e=e*10+ch-'0';ch=getchar();}
	if(ck)e=-e;
}
const int INF=0x3f3f3f3f;
inline void input();
struct edge{
	int u,v,cap,flow,cost;
};
struct Answer{
	int flow,cost;
}Ans;
struct mcmf{
	int n,m;
	vector <edge> edges;
	vector <int> g[maxn];
	int d[maxn],p[maxn],inq[maxn],a[maxn];
	inline void init(int n)
	{
		this->n=n;
		for (int i=0;i<n;++i) g[i].clear();
		edges.clear();
	}
	inline void ad(int u,int v,int cap,int cost)
	{
		edges.push_back((edge){u,v,cap,0,cost});
		edges.push_back((edge){v,u,0,0,-cost});
		m=edges.size();
		g[u].push_back(m-2);g[v].push_back(m-1);
	}
	inline bool spfa(int s,int t,int &flow,int &cost)
	{
		memset(d,INF,sizeof d);
		memset(inq,0,sizeof inq);
		d[s]=0;inq[s]=1;p[s]=0;a[s]=INF;
		queue <int> q;
		q.push(s);
		while (!q.empty())
		{
			int u=q.front();q.pop();
			inq[u]=0;
			int siz=g[u].size();
			for (int i=0;i<siz;++i)
			{
				edge &e=edges[g[u][i]];
				if (e.cap>e.flow && d[e.v]>d[u]+e.cost)
				{
					d[e.v]=d[u]+e.cost;
					p[e.v]=g[u][i];
					a[e.v]=min(a[u],e.cap-e.flow);
					if (!inq[e.v]) q.push(e.v),inq[e.v]=1;
				}
			}
		}
		if (d[t]==INF) return false;
		flow+=a[t];
		cost+=a[t]*d[t];
		int u=t;
		while (u!=s)
		{
			edges[p[u]].flow+=a[t];
			edges[p[u]^1].flow-=a[t];
			u=edges[p[u]].u;
		}
		return true;
	}
	Answer mincost(int s,int t)
	{
		int flow=0,cost=0;
		while (spfa(s,t,flow,cost));
		return (Answer){flow,cost};
	}
}mc;
int n,m,t1,t2;
int cnt[25][25];
int st[25][25],en[25][25];
char ss[25];
int dx[]={-1,0,1,-1,1,1,-1,0,1};
int dy[]={1,1,1,0,0,-1,-1,-1};
int main()
{
	input();
	int S=3*n*m+5;int T=S+1;
	int N=n*m+3;
	mc.init(T+5);
	for (int i=1;i<=n;++i)
		for (int j=1;j<=m;++j)
		{
			if (st[i][j]==1 && en[i][j]==0)
			{
				mc.ad(S,(i-1)*m+j,1,0);
				mc.ad((i-1)*m+j,(i-1)*m+j+N,(cnt[i][j]+1)/2,0);
			}
			else if (st[i][j]==0 && en[i][j]==1)
			{
				mc.ad((i-1)*m+j,T,1,0);
				mc.ad((i-1)*m+j,(i-1)*m+j+N,(cnt[i][j]+1)/2,0);
			}
			else
				mc.ad((i-1)*m+j,(i-1)*m+j+N,cnt[i][j]/2,0);
			for (int k=0;k<8;++k)
			{
				int nx=i+dx[k],ny=j+dy[k];
				if (nx<1||ny<1||nx>n||ny>m) continue;
				mc.ad((i-1)*m+j+N,(nx-1)*m+ny,INF,1);
			}
		}
	Ans=mc.mincost(S,T);
	if ((Ans.flow+t2)==t1) printf(
	else puts("-1");
	return 0;
}
inline void input()
{
	read(n),read(m);
	for (int i=1;i<=n;++i)
	{
		scanf(
		for (int j=1;j<=m;++j)
		{
			st[i][j]=ss[j]-'0';
			if (st[i][j]==1) ++t1;
		}
	}
	for (int i=1;i<=n;++i)
	{
		scanf(
		for (int j=1;j<=m;++j)
		{
			en[i][j]=ss[j]-'0';
			if (st[i][j]==1 && en[i][j]==st[i][j]) ++t2;
		}
	}
	for (int i=1;i<=n;++i)
	{
		scanf(
		for (int j=1;j<=m;++j)
			cnt[i][j]=ss[j]-'0';
	}
}