[清华集训2017]无限之环(费用流)
题目:洛谷P4003、LOJ#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;
}

浙公网安备 33010602011771号