[CQOI2012][洛谷P3159][BZOJ2668]交换棋子(网络流+图论)

Address

洛谷P3159

BZOJ2668

Solution

  • 显然交换两个同色的棋子是不优的。
  • 那么我们可以把黑棋看作空地,把交换相邻两个棋子看作:将一个白棋移动到相邻的某个空地中。
  • 那么整个过程就可以看作:一开始棋盘上全是空地,然后我们在某些位置上放白棋,并进行一系列的移动。棋盘达到目标局面时,把棋盘上的白棋全部拿走。
  • 考虑用费用流解决,先建立源点 \(S\) 和 汇点 \(T\)
  • 因为有交换次数的限制,所以把每个点 \(x\) 拆成 \(3\) 个点:\(in(x), out(x), mid(x)\)
  • \(a[x]\) 表示 \(\lfloor\) 将相邻的白棋移到位置 \(x\) \(\rfloor\) 这一操作的次数。
  • \(b[x]\) 表示 \(\lfloor\) 将位置 \(x\) 的白棋移到相邻位置 \(\rfloor\) 这一操作的次数。
  • \(c[x]\) 表示 \(\lfloor\) 将相邻的白棋移到位置 \(x\) \(\rfloor\)\(\lfloor\) 将位置 \(x\)的白棋移到相邻位置 \(\rfloor\) 这两个操作的次数之和的上限,也就是输入的第 \(3\) 个矩阵。
  • 如果初始和目标局面中,\(x\) 均为白棋或均为空地,那么 \(a[x]=b[x]\),所以 \(a[x],b[x]\)上限均为 \(\lfloor\frac{c[x]}{2}\rfloor\)
  • 如果 \(x\) 初始为白棋,目标为空地,那么 \(a[x]+1=b[x]\),那么 \(a[x]\)上限\(\lfloor\frac{c[x]}{2}\rfloor\)\(b[x]\)上限\(\lceil\frac{c[x]}{2}\rceil\)
  • 如果 \(x\) 初始为空地,目标为白棋,那么 \(b[x]+1=a[x]\),那么 \(a[x]\)上限\(\lceil\frac{c[x]}{2}\rceil\)\(b[x]\)上限\(\lfloor\frac{c[x]}{2}\rfloor\)
  • 然后连边 \((in(x),mid(x),a[x]的上限,0),(mid(x),out(x),b[x]的上限,0)\)
  • 考虑源点和汇点的连边。
  • 首先我们在某些位置上放白棋:假设在位置 \(x\) 放了 \(1\) 个白棋,那么连边:\((S,mid(x),1,0)\)
  • 同理,如果最后要在位置 \(x\) 拿走 \(1\) 个白棋,那么连边:\((mid(x),T,1,0)\)
  • 最后对于任意相邻两点 \(x,y\),连边 \((out(x),in(y),∞,0)\),表示白棋的转移。
  • 求最小费用最大流就是答案。
  • 注意特判无解:初始和目标局面白棋个数不同,或者最大流小于白棋个数。

Code

#include <bits/stdc++.h>

using namespace std;

const int e = 1e6 + 5, o = 1e3 + 5, dx[] = {1, 0, -1, 0, 1, -1, -1, 1};
const int inf = 0x3f3f3f3f, dy[] = {0, 1, 0, -1, 1, -1, 1, -1};
bool vis[e];
int adj[e], nxt[e], pre[e], num = 1, go[e], n, m, s, t, nm, a[e], frm[e], c[e], f[e], d[e];
int ans, co[e], cs, ct, bs[o][o], bt[o][o], mxf;

inline void add(int x, int y, int v, int w)
{
	nxt[++num] = adj[x]; frm[num] = x; adj[x] = num; go[num] = y; c[num] = v; co[num] = w;
	nxt[++num] = adj[y]; frm[num] = y; adj[y] = num; go[num] = x; co[num] = -w; 
}

inline bool bfs()
{
	queue<int>q;
	int i;
	for (i = 1; i <= t; i++) d[i] = inf;
	d[s] = pre[s] = 0; a[s] = inf;
	q.push(s);
	while (!q.empty())
	{
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for (i = adj[u]; i; i = nxt[i])
		{
			int v = go[i];
			if (c[i] > f[i] && d[v] > d[u] + co[i])
			{
				d[v] = d[u] + co[i];
				pre[v] = i;
				a[v] = min(a[u], c[i] - f[i]);
				if (!vis[v]) vis[v] = 1, q.push(v);
			}
		}
	}
	if (d[t] == inf) return 0;
	ans += d[t] * a[t];
	mxf += a[t]; 
	int u = t;
	while (u != s)
	{
		f[pre[u]] += a[t];
		f[pre[u] ^ 1] -= a[t];
		u = frm[pre[u]];
	}
	return 1;
}

inline int in(int x, int y)
{
	return (x - 1) * m + y;
}

inline int mid(int x, int y)
{
	return (x - 1) * m + y + nm;
}

inline int out(int x, int y)
{
	return (x - 1) * m + y + 2 * nm;
}

int main()
{
	scanf("%d%d", &n, &m); nm = n * m; s = 3 * nm + 1; t = s + 1;
	int i, j, x, k; 
	for (i = 1; i <= n; i++)
	for (j = 1; j <= m; j++)
	{
		char ch;
		cin >> ch;
		bs[i][j] = ch - '0';
	}
	for (i = 1; i <= n; i++)
	for (j = 1; j <= m; j++)
	{
		char ch;
		cin >> ch;
		bt[i][j] = ch - '0';
	}
	for (i = 1; i <= n; i++)
	for (j = 1; j <= m; j++)
	{
		char ch;
		cin >> ch;
		x = ch - '0';
		if (bs[i][j]) add(s, mid(i, j), 1, 0), cs++;
		if (bt[i][j]) add(mid(i, j), t, 1, 0), ct++;
		if (bs[i][j] == bt[i][j])
		{
			add(in(i, j), mid(i, j), x / 2, 0);
			add(mid(i, j), out(i, j), x / 2, 0);
		}
		else if (bs[i][j])
		{
			add(in(i, j), mid(i, j), x / 2, 0);
			add(mid(i, j), out(i, j), (x + 1) / 2, 0);
		}
		else
		{
			add(in(i, j), mid(i, j), (x + 1) / 2, 0);
			add(mid(i, j), out(i, j), x / 2, 0);
		}
		for (k = 0; k < 8; k++)
		{
			int x = dx[k] + i, y = dy[k] + j;
			if (x < 1 || y < 1 || x > n || y > m) continue;
			add(out(i, j), in(x, y), inf, 1);
		}
	}
	if (cs != ct)
	{
		puts("-1");
		return 0;
	}
	while (bfs());
	if (mxf != ct) puts("-1");
	else cout << ans << endl;
	return 0;
}
posted @ 2020-01-15 12:38  花淇淋  阅读(172)  评论(0编辑  收藏  举报