题意

给出一个有向图,并给出仙人掌图的定义

  1. 图本身是强连通的
  2. 每条边属于且只属于一个环

判断输入的图是否是强连通的。

分析

杭电OJ上的数据比较弱,网上一些有明显错误的代码也能AC。

本着求真务实的精神,取网上查阅了相关资料,整理出来一个对自己来说还比较明确的算法。

从DFS森林说起

从有向图的某一点开始进行深度优先遍历,按照遍历的先后顺序会形成一棵树,像这种边被称作树边(Tree Edge)

当然有向图中还可能会存在一些其他的边:

  • 从当前节点连向其祖先节点的边叫做反向边(Back Edge)
  • 从当前节点连向其后代节点的边叫做前向边(Forward Edge)
  • 从当前节点连向其他节点,可能是某个祖先其他分支的节点或者另一颗DFS树的节点,这种边叫做交叉边(Cross Edge)

按边的分类考虑仙人掌图

接下来默认图是强连通的,后面不再强调。

  1. 如果\(u \to v\)是一条前向边,必然有一条从\(v\)到\(u\)的路径\(Path\)。这样\(Path\)就和前向边\(u \to v\)构成了一个环,同时也和树边上的\(u\)到\(v\)的路径构成了一个环,而且这两个环有公共路径\(Path\)。因此得到结论:仙人掌图中不含前向边
  2. 如果\(u \to v\)是一条交叉边,它们的最近公共祖先为\(anc\)。同样也有一条从\(v\)到\(anc\)的路径\(Path_{v \to anc}\),这条路径和\(v\)到\(anc\)的路径或相交或不相交。同样也构成了两个有公共边的环,因此得到结论:仙人掌图中不含交叉边

因此,除了树边只剩下反向边,而且可以看出每有一条反向边\(u \to v\),它和树边上的路径\(v \to u\)构成了一个环。

下面想办法保证每条树边至多被一个环所包含:

  • 一个点最多有一条反向边
  • 在当前节点记录一个可以返回的最小的DFS序,保证反向边指向的节点的DFS序不能小于该值,否则会出现有公共边的两个环。

这是通过一遍DFS实现的。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#include <vector>
using namespace std; const int maxn = 10000 + 10; int n, m;
vector<int> G[maxn]; stack<int> S;
int dfs_clock, pre[maxn], low[maxn];
int scc_cnt, sccno[maxn]; void dfs(int u) {
pre[u] = low[u] = ++dfs_clock;
S.push(u);
for(int v : G[u]) {
if(!pre[v]) {
dfs(v);
low[u] = min(low[u], low[v]);
} else if(!sccno[v]) low[u] = min(low[u], pre[v]);
}
if(low[u] == pre[u]) {
scc_cnt++;
for(;;) {
int x = S.top(); S.pop();
sccno[x] = scc_cnt;
if(x == u) break;
}
}
} //Tarjan算法求强连通分量
void find_scc() {
dfs_clock = scc_cnt = 0;
memset(pre, 0, sizeof(pre));
memset(sccno, 0, sizeof(sccno));
for(int i = 0; i < n; i++) if(!pre[i])
dfs(i);
} //第二遍DFS保证是仙人掌图 //color[u]为0表示还没有访问,为1表示正在访问,为2表示已经访问完毕
int color[maxn]; bool dfs2(int u, int minBack) { //minBack表示反向边能指向的最小的DFS序
color[u] = 1;
int backs = 0;//反向边的个数,至多只能有一个
for(int v : G[u]) if(color[v] == 1) { //找到一条反向边
backs++;
if(backs > 1) return false;
if(pre[v] < minBack) return false; //反向边指向的节点的DFS序小于最小值
}
if(backs) minBack = pre[u];
for(int v : G[u]) {
if(color[v] == 2) return false; //前向边或交叉边
if(color[v] == 0) //树边
if(!dfs2(v, minBack)) return false;;
}
color[u] = 2;
return true;
} int main()
{
int T; scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++) G[i].clear();
while(m--) {
int u, v; scanf("%d%d", &u, &v);
G[u].push_back(v);
} find_scc();
if(scc_cnt > 1) { puts("NO"); continue; } memset(color, 0, sizeof(color));
if(!dfs2(0, 0)) puts("NO");
else puts("YES");
} return 0; }

参考资料

1.仙人掌图分析

2.my solution注:这份代码没有考虑只能有一条反向边的限制,但也能在UVa上AC

HDU 3594 Cactus 有向仙人掌图判定的更多相关文章

  1. hdu 3594 Cactus /uva 10510 仙人掌图判定

    仙人掌图(有向):同时满足:1强连通:2任何边不在俩个环中. 个人理解:其实就是环之间相连,两两只有一个公共点,(其实可以缩块),那个公共点是割点.HDU数据弱,网上很多错误代码和解法也可以过. 个人 ...

  2. hdu 3594 强连通好题仙人掌图,对自己的tarjan模板改下用这个

    #include<stdio.h> #include<string.h> #define N 21000 struct node { int v,next; }bian[510 ...

  3. 仙人掌图判定及求直径HDU3594 BZOJ1023

    https://wenku.baidu.com/view/ce296043192e45361066f575.html   //仙人掌图基础知识3个判定条件 http://blog.csdn.net/y ...

  4. HDU 3594.Cactus 仙人掌图

    Cactus Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Subm ...

  5. HDU 3594 Cactus (强连通+仙人掌图)

    <题目链接> <转载于 >>> > 题目大意: 给你一个图,让你判断他是不是仙人掌图. 仙人掌图的条件是: 1.是强连通图. 2.每条边在仙人掌图中只属于一个 ...

  6. HDU - 3594 Cactus

    这是一个有向仙人掌的题目,要求判定给定的图是不是强连通图,而且每一条边只能出现在一个环中,这里有一个介绍有向仙人掌的文档:http://files.cnblogs.com/ambition/cactu ...

  7. HDU 3594 Cactus(仙人掌问题)

    http://acm.hdu.edu.cn/showproblem.php?pid=3594 题意: 一个有向图,判断是否强连通和每条边只在一个环中. 思路: 仙人掌问题. 用Tarjan算法判断强连 ...

  8. hdu - 3594 Cactus (强连通)

    http://acm.hdu.edu.cn/showproblem.php?pid=3594 判断给定的图是否是强连通的,并且每条边都只属于一个连通分量. 判断强连通只需要判断缩点之后顶点数是否为1即 ...

  9. HDU 3594 Cactus (强连通分量 + 一个边只能在一个环里)

    题意:判断题目中给出的图是否符合两个条件.1 这图只有一个强连通分量 2 一条边只能出现在一个环里. 思路:条件1的满足只需要tarjan算法正常求强连通分量即可,关键是第二个条件,我们把对边的判断转 ...

随机推荐

  1. Vue双向绑定简单实现

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. Kendo MVVM (一) 概述

    Kendo MVVM (一) 概述 Model View ViewModel (MVVM)  是开发人员经常使用的一种设计模式,以实现数据模型(Model)和视图(View)的分离.MVVM 中的 V ...

  3. malloc/free函数

    一.malloc 函数原型:void *malloc(unsigned int size); 功       能:在内存的动态存储区中分配一个长度为size的连续空间. 返  回 值:指向所分配的连续 ...

  4. 【Python图像特征的音乐序列生成】关于小样本的一些思考

    我之前就注意到,深度学习和音乐结合,尤其是从乐理出发进行结合(而不是纯粹的进行音乐生成),是一个尚未被深度挖掘的全新领域.可想而知,这个方向符合我要求的数据肯定是要自己搜集了. 自己搜集的数据,在量上 ...

  5. BZOJ 4423: [AMPPZ2013]Bytehattan 并查集+平面图转对偶图

    4423: [AMPPZ2013]Bytehattan Time Limit: 3 Sec  Memory Limit: 128 MB Submit: 277  Solved: 183 [Submit ...

  6. MovieReview—Wile Hunter(荒野猎人)

     Faith is Power         Faith is power, this sentence is not wrong. Find your own beliefs, and strug ...

  7. spring security 2.x HttpSessionEventPublisher 以及listener配置

    在环境为spring security2.x时 *JDK6 spring 2* 正确的filter路径是:org.springframework.security.ui.session.HttpSes ...

  8. HDU 5452 Minimum Cut (Spaning Tree)

    生成树的上的一个非根结点对应一条生成树上的边,然后这个结点的子树上连出去的边就对应去掉这条边的割, 然后就可以对树外的边求LCA,在LCA上标记,利用这个信息可以算出有多少条边在子树上,以及有多少条边 ...

  9. C++容器类-list

    C++ 表(List容器类) 一.概念 头文件:#include <list> 又叫链表,是一种双线性链表,只能顺序访问(从前往后或从后往前) 他不支持随机访问. 二.方法 #includ ...

  10. Java写诗程序

    import java.util.Random; public class test_word { public static void main(String[] args) { System.ou ...