1 条题解
-
0
题意
给定一张 个点 条边的有向图 ,求一个导出子图 (即先选定一个点集 ,然后对于 ,若 那么有 ),使得对于 , 在 中的入度和出度都恰好为一。输出一个方案或报告无解。
,,保证图中无重边和自环。
解法
对于一个满足每个点入度出度均为 的图,显然它就是一个环。不妨考虑这么构造:对于点 ,先满足它的出度为 ,那么 有且仅有一条出边连向某一个点。这条边必须连向另外一个点 ,若 还没有入边和出边,连边后继续考虑点 ;若 已经有了入边,那么连接后无法满足入度为 ,不可取;若 没有入边但有一条出边,即 是第一个考虑的点,那么连接 后就完成了构图。这样构造出的图就是一个环。
然后朴素的想法就是:在 中选一个环出来输出就是答案。下面是一个反例:

对于这张图, 即为一个环。但如果我们选取 ,那么导出子图就会包含 这条边,不满足子图中每个点入度、出度均为 ;而正确答案即为环长更小的 。 不合法的原因就是出现了环套环。我们假设已经找到了原图中的所有环,那么对于环长最小的环显然不会出现环套环的情况,因为对于一个被套了的环环长一定更短。那么我们考虑找到环长最短的环即可。
实现
这里是一个找环的套路:考虑 dfs。我们维护一个栈,访问节点时将其入栈,回溯时将其出栈。对于点 我们分别记录它是否被访问过和是否在栈中(前者当 出栈时仍然保留)。对于点 和边 ,如果 已经在栈中,意味着我们已经找到了一个长成 的环,中间省略的点即为从 走到 路径上压入栈里的节点。然后在所有环中找到环长最小的即可。初始时我们枚举 中还没被访问过的点作为起点。
代码
#include <bits/stdc++.h> using namespace std; int n, m; const int maxm = 2005, maxn = 1005; namespace Graph { struct Edge { int to, nxt; } e[maxm]; int head[maxn], ecnt = 0; void addEdge(int u, int v) { e[++ ecnt] = Edge { v, head[u] }; head[u] = ecnt; } } using namespace Graph; int siz; vector<int> ans; int st[maxn], top; bool vis[maxn], used[maxn]; void dfs(int u) { if (used[u]) { if (!vis[u]) return ; int now_siz = 1, i; for (i = top; st[i] != u; i --, now_siz ++); if (siz <= now_siz) return ; siz = now_siz, ans.clear(); for (; i <= top; i ++) ans.push_back(st[i]); return ; } vis[st[++ top] = u] = used[u] = 1; for (int i = head[u]; i; i = e[i].nxt) dfs(e[i].to); vis[st[top --]] = 0; } int main() { scanf("%d %d", &n, &m), siz = n + 1; for (int i = 1, u, v; i <= m; i ++) scanf("%d %d", &u, &v), addEdge(u, v); for (int i = 1; i <= n; i ++) if (!used[i]) dfs(i); if (siz == n + 1) return puts("-1"), 0; printf("%d\n", siz); for (auto x : ans) printf("%d\n", x); return 0; }
- 1
信息
- ID
- 11750
- 时间
- 2000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者