[POJ 1637] Sightseeing tour(网络流)

题意

(混合图的欧拉回路判定)

给你一个既存在有向边, 又存在无向边的图. 问是否存在欧拉回路.

N200,M1000

题解

难点在于无向边. 考虑每个点的度数限制.

我们先对无向边任意定向, 现在每个点都有一个出度和入度的差; 而我们要求最终每个点出度和入度相等.

令它出度减去入度为 deg ,如果 deg 为奇数那么必不存在欧拉回路,因为每次我们修改一条边的定向,会使得入度 +1 出度 1 (或者相反)。那么变化后的 deg 总为奇数,就绝不可能成为 0 了。

然后接下来我们就需要判定之后改变边的定向能否满足要求。因为范围较小,我们继续可以联想到网络流。

首先我们建出超级源 S 、超级汇 T

  1. 把超级源向所有 deg>0 的点 p 连上 Sp 流量为 deg2 的边。
  2. 把所有 deg<0 的点 p 向超级汇连上 pT 流量为 deg2 的边。
  3. 把原图中存在的无向边我们连上 uv 流量为 1 的边。注意此处连 uv 的话,需要一开始假设最初的定向就是 uv 使得 ++ deg[u], -- deg[v];

然后跑一遍网络流得到的流量如果和所有 deg>0 的点的 deg 和相同,那么就是存在一组解的,否则就必不存在。

考虑为什么是这样的,因为我们每次流一条边,相当于把一条边 uv 反向成 vu ,使得给 u 的入度增加 1 出度减少 1 ,所以 deg 会直接减少 2 ,这就是为什么我们一开始流量为 |deg2| 的原因。

如果满流的结果刚好是 deg>0deg ,意味着所有点都修改正确了。

总结

对于有向图欧拉回路的题,牢记 入度 = 出度 才存在的性质。

代码

#include<cstdio> #include<iostream> #include<queue> #include<cstdlib> #include<cstring> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << x << endl #define DEBUG(...) fprintf(stderr, __VA_ARGS__) using namespace std; inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;} inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() { int x = 0, fh = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1; for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48); return x * fh; } void File() { #ifdef zjp_shadow freopen ("1637.in", "r", stdin); freopen ("1637.out", "w", stdout); #endif } int n, m; const int N = 410, M = 5010, inf = 0x7f7f7f7f; namespace Dinic { int Head[N], Next[M], to[M], cap[M], e = 1; inline void add_edge(int u, int v, int flow) { to[++ e] = v; Next[e] = Head[u]; Head[u] = e; cap[e] = flow; } inline void Add(int u, int v, int flow) { add_edge(u, v, flow); add_edge(v, u, 0); } int dis[N], S, T; bool Bfs() { queue<int> Q; Set(dis, 0); dis[S] = 1; Q.push(S); while (!Q.empty()) { int u = Q.front(); Q.pop(); if (u == T) return true; for (int i = Head[u]; i; i = Next[i]) if (cap[i]) { int v = to[i]; if (!dis[v]) dis[v] = dis[u] + 1, Q.push(v); } } return false; } int cur[N]; int Dfs(int u, int flow) { if (u == T || !flow) return flow; int res = 0, f; for (int &i = cur[u]; i; i = Next[i]) if (cap[i]) { int v = to[i]; if (dis[v] != dis[u] + 1) continue ; if ((f = Dfs(v, min(flow, cap[i])))) { cap[i] -= f; cap[i ^ 1] += f; res += f; if (!(flow -= f)) break; } } if (!res || !flow) dis[u] = 0; return res; } int Run() { int sum_flow = 0; while (Bfs()) Cpy(cur, Head), sum_flow += Dfs(S, inf); return sum_flow; } } using namespace Dinic; int deg[N]; inline bool IsOdd() { For (i, 1, n) if (deg[i] & 1) return true; return false; } int main () { File(); for (int cases = read(); cases; -- cases) { n = read(); m = read(); e = 1; For (i, 1, n + 2) Head[i] = deg[i] = 0; For (i, 1, m) { int u = read(), v = read(), type = read(); ++ deg[u]; -- deg[v]; if (!type) Add(u, v, 1); } if (IsOdd()) { puts("impossible"); continue ; } S = n + 1, T = S + 1; int tot = 0; For (i, 1, n) { if (deg[i] > 0) Add(S, i, deg[i] >> 1), tot += deg[i] >> 1; if (deg[i] < 0) Add(i, T, (- deg[i]) >> 1); } puts(Run() == tot ? "possible" : "impossible"); } return 0; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/9568078.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(340)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示