HDU 3594 Cactus 有向仙人掌图判定
题意
给出一个有向图,并给出仙人掌图的定义
- 图本身是强连通的
- 每条边属于且只属于一个环
判断输入的图是否是强连通的。
分析
杭电OJ上的数据比较弱,网上一些有明显错误的代码也能AC。
本着求真务实的精神,取网上查阅了相关资料,整理出来一个对自己来说还比较明确的算法。
从DFS森林说起
从有向图的某一点开始进行深度优先遍历,按照遍历的先后顺序会形成一棵树,像这种边被称作树边(Tree Edge)
当然有向图中还可能会存在一些其他的边:
- 从当前节点连向其祖先节点的边叫做反向边(Back Edge)
- 从当前节点连向其后代节点的边叫做前向边(Forward Edge)
- 从当前节点连向其他节点,可能是某个祖先其他分支的节点或者另一颗DFS树的节点,这种边叫做交叉边(Cross Edge)

按边的分类考虑仙人掌图
接下来默认图是强连通的,后面不再强调。
- 如果\(u \to v\)是一条前向边,必然有一条从\(v\)到\(u\)的路径\(Path\)。这样\(Path\)就和前向边\(u \to v\)构成了一个环,同时也和树边上的\(u\)到\(v\)的路径构成了一个环,而且这两个环有公共路径\(Path\)。因此得到结论:仙人掌图中不含前向边。
- 如果\(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 有向仙人掌图判定的更多相关文章
- hdu 3594 Cactus /uva 10510 仙人掌图判定
仙人掌图(有向):同时满足:1强连通:2任何边不在俩个环中. 个人理解:其实就是环之间相连,两两只有一个公共点,(其实可以缩块),那个公共点是割点.HDU数据弱,网上很多错误代码和解法也可以过. 个人 ...
- hdu 3594 强连通好题仙人掌图,对自己的tarjan模板改下用这个
#include<stdio.h> #include<string.h> #define N 21000 struct node { int v,next; }bian[510 ...
- 仙人掌图判定及求直径HDU3594 BZOJ1023
https://wenku.baidu.com/view/ce296043192e45361066f575.html //仙人掌图基础知识3个判定条件 http://blog.csdn.net/y ...
- HDU 3594.Cactus 仙人掌图
Cactus Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Subm ...
- HDU 3594 Cactus (强连通+仙人掌图)
<题目链接> <转载于 >>> > 题目大意: 给你一个图,让你判断他是不是仙人掌图. 仙人掌图的条件是: 1.是强连通图. 2.每条边在仙人掌图中只属于一个 ...
- HDU - 3594 Cactus
这是一个有向仙人掌的题目,要求判定给定的图是不是强连通图,而且每一条边只能出现在一个环中,这里有一个介绍有向仙人掌的文档:http://files.cnblogs.com/ambition/cactu ...
- HDU 3594 Cactus(仙人掌问题)
http://acm.hdu.edu.cn/showproblem.php?pid=3594 题意: 一个有向图,判断是否强连通和每条边只在一个环中. 思路: 仙人掌问题. 用Tarjan算法判断强连 ...
- hdu - 3594 Cactus (强连通)
http://acm.hdu.edu.cn/showproblem.php?pid=3594 判断给定的图是否是强连通的,并且每条边都只属于一个连通分量. 判断强连通只需要判断缩点之后顶点数是否为1即 ...
- HDU 3594 Cactus (强连通分量 + 一个边只能在一个环里)
题意:判断题目中给出的图是否符合两个条件.1 这图只有一个强连通分量 2 一条边只能出现在一个环里. 思路:条件1的满足只需要tarjan算法正常求强连通分量即可,关键是第二个条件,我们把对边的判断转 ...
随机推荐
- 低版本Firefox支持innerText属性兼容方法
FireFox支持innerText属性了,很遗憾是44.0.2版本以下还需要兼容处理. 方法一: innerHTML是符合W3C标准的属性,而innerText只适用于IE浏览器,因此,尽可能地去使 ...
- ef 操作 mysql 中文乱码问题
1.保证mysql数据的编码为utf8 启动mysql mysql -hlocalhost -uroot -p 输入密码 show VARIABLES like 'character_%'; SET ...
- 文件系统结构-《循序渐进linux》
1.目录结构 很多linux的发行版都遵循FSSTND标准,这一标准仅包含系统最基本的文件. /dev 设备文件 /bin 可执行的二进制文件 /opt /root 超级用户的主目录 /home 每个 ...
- ae(ArcEngine) java swing开发入门系列(2):ae的类型转换和Proxy类说明
做过C#版ae的都知道,操作同一个“对象”,用他的不同功能要转换到相应的接口,但java版有时不能直接做类型转换 例如下图在C#是可以的 但在java不行,这样转会报错,看IFeatureClass的 ...
- MFC制作简单通讯录程序
学习c++和MFC一段时间了,苦于没有项目实战,所以自己写了一个简单的简单通讯录程序,以前用c#写简单很多,例程是这本书上的实例,我的第一个winform程序也是从这本书上学的,总结c#写的话更简单, ...
- 在开发第一个Android应用之前需要知道的5件事:
你能否详细讲述一下,在开发Android应用过程中每一阶段要用到的技能和编程语言? 建立一个Android应用程序可以归结为两个主要技能/语言:Java和Android系统.Java是Android的 ...
- Elasticsearch-基本操作1
Elasticsearch版本:6.0 一.文档 一个文档不仅包含数据,也包含元数据,三个必须的元数据如下 _index:具有共同特性分到一起的文档集合,标示了文档的存放位置: 名字小写,不以下划线开 ...
- GIT SSH免登录密码实现更新(git pull)、推送(git push)操作
一.使用场景 现在有两台服务器A和B,在A服务器上搭建有git版本代码仓库,现要实现B服务器SSH免密码登录A服务器,并能够从A服务器拉取.推送代码! 二.操作步骤 1.在B服务器项目根目录下执行以 ...
- Oracle关于TX锁的一个有趣的问题
前阵子有一个网友在群里问了一个关于Oracle数据库的TX锁问题,问题原文如下: 请教一个问题: 两个会话执行不同的delete语句,结果都是删除同一个行.先执行的会话里where条件不加索引走全表扫 ...
- js构造方法
<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Java ...