[清华集训2017]无限之环(费用流)

题目:洛谷P4003LOJ#2321

题目描述:

在一个\(n\times m\)的网格中,每个格子可能没有水管,也有可能有\(1\)种形态的水管

一共有\(15\)种形态的水管,一个四位的二进制数\(x\)从低到高位分别表示这个水管有没有与上、右、下、左联通,\(x=0\)表示这个地方没有水管,你可以花费\(1\)代价去将一个不为直线型(即\(x\neq 5\)\(x\neq 10\))的水管顺时针或逆时针旋转\(90 ^{\circ}\),问要使所有水管都不漏水,即不存在一个水管有一个方向指向空处需要至少多少代价,不存在合理操作输出\(-1\)

\(n\times m \leq 2000\)

蒟蒻题解:

要让一个水管不漏水,说明一个水管往它的一个接口方向还有一个其他水管的接口

如果强制让每个水管往它的接口均有流量,那么就能保证联通,可以将网格黑白染色,黑格的流量会流到白格,白格的流量会流到黑格,所以黑格的接口总数要和白格接口总数相等,不相等那么一定无解

要让它流量平衡,不妨让源点\(S\)连向所有有水管的黑格,流量为当前水管的接口个数,费用为\(0\),让所有有水管的白格连向汇点\(T\),流量为当前水管的接口个数,费用为\(0\),然后把黑格的接口连向四周白格的接口,流量均为\(1\),费用均为\(0\),这样只要满足黑格的流量能全部流到白格,即满流,那么当前的局面就是不会漏水的

怎么处理旋转的情况呢?

模拟一下,可以发现:

当一个水管只有一个接口时,假设它的接口在上方,那么它接口转到左边和右边都需要消耗\(1\)代价,转到下面需要消耗\(2\)代价,不妨以黑格的水管为例,那么需要将上方的接口连向左边和右边的接口,流量为\(1\),费用为\(1\),将上方的接口连向下方的接口,流量为\(1\),费用为\(2\)

好像有戏?再试试其他情况

当一个水管有\(2\)个接口时,由于直线型水管不能旋转,我们只考虑\(L\)型的水管,假设当前是黑格,\(L\)型水管连向的是上方和右方,那么将它变为上方和左方需要消耗\(1\)代价,将它变为右方和下方需要消耗\(1\)代价,将它变为左方和下方需要消耗\(2\)代价,仔细观察可以发现,只要把上方接口连向下方接口,流量为\(1\),费用为\(1\),右方接口连向左方接口,流量为\(1\),费用为\(1\),那么便能处理了

当一个水管有\(3\)个接口的情况就由读者自己思考(其实就是和\(1\)个接口的情况相反)

当一个水管有\(4\)个接口时,我们怎么旋转都没有意义,所以不需要旋转

缕清思路后,我们就可以开始码代码了,虽然有一点点长,但是其实建图部分很多情况都是类似的,只不过是因为要分类讨论,本蒟蒻太菜了,不知道怎么打比较方便,但是其实很多地方在原来的基础上复制粘贴再改改就可以了,所以也不用打很久

参考程序:

#include<bits/stdc++.h>
using namespace std;
#define Re register int

const int N = 10005;
const int M = 36005;
const int K = 2005;
const int Inf = 1e9;
int n, m, cnt = 1, num, t, sum1, sum2, ans, id[K][K], up[K][K], rt[K][K], dw[K][K], lt[K][K], d[N], hea[N], h[N], nxt[M], to[M], wi[M], cst[M];
bool b[N];
queue<int> q;

inline int read()
{
	char c = getchar();
	int ans = 0;
	while (c < 48 || c > 57) c = getchar();
	while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
	return ans;
}

inline void add(int x, int y, int w, int c)
{
	nxt[++cnt] = hea[x], to[cnt] = y, wi[cnt] = w, cst[cnt] = c, hea[x] = cnt;
	nxt[++cnt] = hea[y], to[cnt] = x, cst[cnt] = -c, hea[y] = cnt;
}

inline bool spfa()
{
	for (Re i = 0; i <= t; ++i) h[i] = hea[i], d[i] = Inf;
	q.push(d[0] = 0);
	while (!q.empty())
	{
		int x = q.front();
		q.pop();
		b[x] = 0;
		for (Re i = hea[x]; i; i = nxt[i])
		{
			int u = to[i];
			if (!wi[i] || d[u] <= d[x] + cst[i]) continue;
			d[u] = d[x] + cst[i];
			if (!b[u]) b[u] = 1, q.push(u);
		}
	}
	return d[t] < Inf;
}

inline int dfs(int x, int y)
{
	if (x == t) return y;
	b[x] = 1;
	int res = y;
	for (Re &i = h[x]; i; i = nxt[i])
	{
		int u = to[i];
		if (b[u] || !wi[i] || d[u] ^ d[x] + cst[i]) continue;
		int v = dfs(u, min(res, wi[i]));
		if (v)
		{
			wi[i] -= v, wi[i ^ 1] += v, res -= v, ans += v * cst[i];
			if (!res) break;
		}
	}
	b[x] = 0;
	return y - res;
}

int main()
{
	n = read(), m = read(), t = n * m * 5 + 1;
	for (Re i = 1; i <= n; ++i)
		for (Re j = 1; j <= m; ++j)
		{
			id[i][j] = ++num, up[i][j] = num + n * m, rt[i][j] = num + n * m * 2, dw[i][j] = num + n * m * 3, lt[i][j] = num + n * m * 4;
			int u = read(), v = u, w = 0;
			if (!u) continue;
			while (u)
			{
				if (u & 1) ++w;
				u >>= 1;
			}
			if ((i ^ j) & 1)
			{
				add(0, num, w, 0);
				if (i > 1) add(up[i][j], dw[i - 1][j], 1, 0);
				if (j > 1) add(lt[i][j], rt[i][j - 1], 1, 0); 
				sum1 += w;
				if (v & 1) add(num, up[i][j], 1, 0);
				if (v & 2) add(num, rt[i][j], 1, 0);
				if (v & 4) add(num, dw[i][j], 1, 0);
				if (v & 8) add(num, lt[i][j], 1, 0);
				if (v == 15 || v == 5 || v == 10) continue;
				if (w == 1)
				{
					if (v & 1) add(up[i][j], rt[i][j], 1, 1), add(up[i][j], lt[i][j], 1, 1), add(up[i][j], dw[i][j], 1, 2);
					else if (v & 2) add(rt[i][j], up[i][j], 1, 1), add(rt[i][j], dw[i][j], 1, 1), add(rt[i][j], lt[i][j], 1, 2);
					else if (v & 4) add(dw[i][j], rt[i][j], 1, 1), add(dw[i][j], lt[i][j], 1, 1), add(dw[i][j], up[i][j], 1, 2);
					else add(lt[i][j], up[i][j], 1, 1), add(lt[i][j], dw[i][j], 1, 1), add(lt[i][j], rt[i][j], 1, 2);
				}
				else if (w == 2)
				{
					if (v & 1) add(up[i][j], dw[i][j], 1, 1);
					if (v & 2) add(rt[i][j], lt[i][j], 1, 1);
					if (v & 4) add(dw[i][j], up[i][j], 1, 1);
					if (v & 8) add(lt[i][j], rt[i][j], 1, 1);
				}
				else
				{
					if (!(v & 1)) add(rt[i][j], up[i][j], 1, 1), add(lt[i][j], up[i][j], 1, 1), add(dw[i][j], up[i][j], 1, 2);
					else if (!(v & 2)) add(up[i][j], rt[i][j], 1, 1), add(dw[i][j], rt[i][j], 1, 1), add(lt[i][j], rt[i][j], 1, 2);
					else if (!(v & 4)) add(rt[i][j], dw[i][j], 1, 1), add(lt[i][j], dw[i][j], 1, 1), add(up[i][j], dw[i][j], 1, 2);
					else add(up[i][j], lt[i][j], 1, 1), add(dw[i][j], lt[i][j], 1, 1), add(rt[i][j], lt[i][j], 1, 2);
				}
			}
			else
			{
				add(num, t, w, 0);
				if (i > 1) add(dw[i - 1][j], up[i][j], 1, 0);
				if (j > 1) add(rt[i][j - 1], lt[i][j], 1, 0);
				sum2 += w;
				if (v & 1) add(up[i][j], num, 1, 0);
				if (v & 2) add(rt[i][j], num, 1, 0);
				if (v & 4) add(dw[i][j], num, 1, 0);
				if (v & 8) add(lt[i][j], num, 1, 0);
				if (v == 15 || v == 5 || v == 10) continue;
				if (w == 1)
				{
					if (v & 1) add(rt[i][j], up[i][j], 1, 1), add(lt[i][j], up[i][j], 1, 1), add(dw[i][j], up[i][j], 1, 2);
					else if (v & 2) add(up[i][j], rt[i][j], 1, 1), add(dw[i][j], rt[i][j], 1, 1), add(lt[i][j], rt[i][j], 1, 2);
					else if (v & 4) add(rt[i][j], dw[i][j], 1, 1), add(lt[i][j], dw[i][j], 1, 1), add(up[i][j], dw[i][j], 1, 2);
					else add(up[i][j], lt[i][j], 1, 1), add(dw[i][j], lt[i][j], 1, 1), add(rt[i][j], lt[i][j], 1, 2);
				}
				else if (w == 2)
				{
					if (v & 1) add(dw[i][j], up[i][j], 1, 1);
					if (v & 2) add(lt[i][j], rt[i][j], 1, 1);
					if (v & 4) add(up[i][j], dw[i][j], 1, 1);
					if (v & 8) add(rt[i][j], lt[i][j], 1, 1);
				}
				else
				{
					if (!(v & 1)) add(up[i][j], rt[i][j], 1, 1), add(up[i][j], lt[i][j], 1, 1), add(up[i][j], dw[i][j], 1, 2);
					else if (!(v & 2)) add(rt[i][j], up[i][j], 1, 1), add(rt[i][j], dw[i][j], 1, 1), add(rt[i][j], lt[i][j], 1, 2);
					else if (!(v & 4)) add(dw[i][j], rt[i][j], 1, 1), add(dw[i][j], lt[i][j], 1, 1), add(dw[i][j], up[i][j], 1, 2);
					else add(lt[i][j], up[i][j], 1, 1), add(lt[i][j], dw[i][j], 1, 1), add(lt[i][j], rt[i][j], 1, 2);
				}
			}
		}
	if (sum1 ^ sum2)
	{
		puts("-1");
		return 0;
	}
	int fl;
	while (spfa())
	{
		while (fl = dfs(0, sum1))
		{
			sum1 -= fl;
			if (!sum1) break;
		}
		if (!sum1) break;
	}
	if (sum1) puts("-1");
	else printf("%d", ans);
	return 0;
}
posted @ 2021-03-16 09:50  clfzs  阅读(111)  评论(0)    收藏  举报