题目链接
题意:给定一个有向图,问这个图是否为仙人掌图(一条边不属于两个及以上环)
思路:类似构造scc的dfs,判定方法为:
1、必然是一个强连通分量
2、一个环上的节点必然只能经过一次
那么dfs的时候,只要记录下每个结点的父亲结点,如果遇到一个结点之前遍历过了,那么就回退的找到改结点,把环上的结点都+1(注意当前结点不算,因为多个环可以连在一个结点上),然后如果有一个结点超过了2,就肯定不是仙人掌图了
代码:
#include <cstdio> #include <cstring> #include <vector> #include <stack> #include <algorithm> using namespace std; const int N = 20005; vector<int> g[N]; int pre[N], lowlink[N], fa[N], dfs_clock, scc_cnt, cnt[N]; int flag; bool find(int v, int u) { while (fa[u] != v) { cnt[u]++; if (cnt[u] > 1) return false; u = fa[u]; } return true; } bool dfs_scc(int u) { pre[u] = lowlink[u] = ++dfs_clock; for (int i = 0; i < g[u].size(); i++) { int v = g[u][i]; if (!pre[v]) { fa[v] = u; if (!dfs_scc(v)) return false; lowlink[u] = min(lowlink[u], lowlink[v]); } else { lowlink[u] = min(lowlink[u], pre[v]); if (!find(v, u)) return false; } } if (lowlink[u] == pre[u]) { scc_cnt++; if (scc_cnt == 2) return false; } return true; } bool find_scc(int n) { dfs_clock = scc_cnt = 0; memset(cnt, 0, sizeof(cnt)); memset(pre, 0, sizeof(pre)); for (int i = 0; i < n; i++) { if (!pre[i] && !dfs_scc(i)) return false; } return true; } int T, n, m; int main() { scanf("%d", &T); while (T--) { scanf("%d%d", &n, &m); for (int i = 0; i < n; i++) g[i].clear(); int u, v; while (m--) { scanf("%d%d", &u, &v); g[u].push_back(v); } printf("%s\n", find_scc(n) ? "YES" : "NO"); } return 0; }