题解 AT2668 【[AGC017E] Jigsaw】
在博客园食用更佳:https://www.cnblogs.com/PinkRabbit/p/AGC017.html。
注意到,如果
我们可以把每个拼图抽象成一个数对:
对于两块拼图
把所有
则题目即是要求:把这张图分成若干条路径,每条边被恰好经过一次,且路径的起点和终点节点有特殊要求(要贴合桌沿)。
实际上就只有两类点,必须从左侧点出发到达右侧点(但是这张图不是二分图)。
要如何判断是否可行呢?我的想法是:反正已知需要花费的路径数目(通过度数确定),那么跑一下最大费用最大流就行。
这样好像确实是可行的,毕竟
不过题解给出了更优秀的判定方法:
-
首先要保证每个左侧点的出度不比入度少,右侧点入度不比出度少。
-
对于每个弱连通分量,其中左侧点中至少要有一个能作为起点的点:也就是出度比入度多。
只要满足这两个条件就一定可以给出方案了,不难证明是正确的(欧拉回路那套理论)。
#include <cstdio>
#include <vector>
const int MH = 405;
int N, H, deg[MH];
std::vector<int> G[MH];
int ok, vis[MH];
void DFS(int u) {
vis[u] = 1;
if (deg[u]) ok = 1;
for (int v : G[u]) if (!vis[v]) DFS(v);
}
int main() {
scanf("%d%d", &N, &H);
for (int i = 1; i <= N; ++i) {
int a, b, c, d, x, y;
scanf("%d%d%d%d", &a, &b, &c, &d);
if (!c) x = a;
else x = c + H;
if (!d) y = b + H;
else y = d;
++deg[x], --deg[y];
G[x].push_back(y), G[y].push_back(x);
}
for (int i = 1; i <= H; ++i)
if (deg[i] < 0 || deg[i + H] > 0) return puts("NO"), 0;
for (int i = 1; i <= 2 * H; ++i) if (!G[i].empty() && !vis[i]) {
ok = 0, DFS(i);
if (!ok) return puts("NO"), 0;
}
puts("YES");
return 0;
}