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算法正常求强连通分量即可,关键是第二个条件,我们把对边的判断转 ...
随机推荐
- sqlserver跟据当天年月日日期查询数据库当天数据
select * from Client where CONVERT(varchar(100), Cli_Datetime, 23) ='2017-11-06' 在查询之前要对表中datetime类 ...
- Xamarin.Form的坑
首先说到xamarin.Forms的安装,简直是坑+坑+坑,为什么呢,有些坑你完全意想不到,比如说你改名字后报错,比如说上份代码能运行,在这里就不能运行,具体先将坑说说吧 坑1 文件名,动不动就报什么 ...
- PreparementStatement接口
1.SQL注入问题在以前过程中,总是采取拼接SQL语句的方式,来实现数据的增删改查! String Sql=select * from user where username="" ...
- ERwin DM Reverse Engineer 逆向工程介绍
介绍内容:利用ERwin DM进行对本地 Oracle 数据库的逆向工程 ERwin DM Version:7.3 ERwin DM 提供两种方式的逆向工程方法,分别是基于脚本文件和基于数据库. 下面 ...
- 手工恢复OSSIM数据库密码
1,现象 今天需要远程连接ossim的mysql数据库读取些东西,于是登录ossim的终端,发现这个mysql客户端无法直接登录,使用自己安装时候那些口令都不行 alienvault:~# mysql ...
- POJ Dollar Dayz 美元假日(完全背包,常规+大数)
题意:给出整数n和k,n代表拥有的钱数量,k代表有k种工具,其价钱分别为1~k.求n元能有多少种购买的方案. 思路:k最大有100,数量过大,要用大数.其他的基本和完全背包一样. #include & ...
- 2017.10.4 QBXT 模拟赛
题目链接 T1 维护一个单调栈 #include <iostream> #include <cstdio> #define N 500000 #define rep(a,b,c ...
- oracle的clob转换varchar2
time: 2008/02/29 author: skate oracle的clob转换varchar2 今天在做一个表的数据转移的时候,发现要他通过比较clob字段,但大家都知道clob字段是无法比 ...
- Codeforces Round #313 (Div. 2) A.B,C,D,E Currency System in Geraldion Gerald is into Art Gerald's Hexagon Equivalent Strings
A题,超级大水题,根据有没有1输出-1和1就行了.我沙茶,把%d写成了%n. B题,也水,两个矩形的长和宽分别加一下,剩下的两个取大的那个,看看是否框得下. C题,其实也很简单,题目保证了小三角形是正 ...
- javascript日期函数
时间对象是一个我们经常要用到的对象,无论是做时间输出.时间判断等操作时都与这个对象离不开.除开JavaScript中的时间对象外,在VbScript中也有许多的时间对象,而且非常好用.下面还是按照我们 ...