hdu-1269
2015-10-22 21:26
495 查看
迷宫城堡
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 10419 Accepted Submission(s): 4682
[align=left]Problem Description[/align]
为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A房间和B房间,只说明可以通过这个通道由A房间到达B房间,但并不说明通过它可以由B房间到达A房间。Gardon需要请你写个程序确认一下是否任意两个房间都是相互连通的,即:对于任意的i和j,至少存在一条路径可以从房间i到房间j,也存在一条路径可以从房间j到房间i。
[align=left]Input[/align]
输入包含多组数据,输入的第一行有两个数:N和M,接下来的M行每行有两个数a和b,表示了一条通道可以从A房间来到B房间。文件最后以两个0结束。
[align=left]Output[/align]
对于输入的每组数据,如果任意两个房间都是相互连接的,输出"Yes",否则输出"No"。
[align=left]Sample Input[/align]
3 3 1 2 2 3 3 1 3 3 1 2 2 3 3 2 0 0
[align=left]Sample Output[/align]
Yes No 题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=1269 题目考点: 这是一道考查强连通分量的基础题目 解体思路:这个问题搞了好几天,终于对tarjan算法理解了。关于这个算法主要要理解low[],与dfn[]的意思。在解释之前,先说几个性质。1.每个强连通分量之间不可能有公共顶点2.若叶子结点没有到其祖先的路径,那它肯定会独立成为一个强连通分量。接下来应用一下网上我认为最好理解的算法原理解释:tarjan算法的基本框架就是dfs,其基本原理是有向图至少存在一棵深搜子树,其结点集合构成一个强连通分量,这是显然的,因为必定有一个强连通分量最后被dfs,这个强连通分量的结点构成深搜树的一棵子树。有了以上结论后,求强连通分量就有思路了,我们在每棵子树深搜完成后判断这棵子树是否构成强连通分量即可,关键在于如何判断一棵子树是否构成强连通分量。注意到最先搜索完的子树是那些叶子结点,要判断叶子结点是否构成强连通分量很简单,若存在叶子结点与其祖先结点的连边,则该叶子结点不构成强连通分量,否则构成强连通分量。tarjan算法用pre[V]数组和low[V]数组来判断子树是否构成强连通分量,pre[v]保存结点v在先序遍历中的访问顺序,以下统称深度优先数,low[v]保存从v出发能到达的所有结点的最小深度优先数,用叶子结点来解释,v为叶子结点, 当low[v]<pre[v]时,表明存在v到其祖先结点的连边,v不构成强连通分量; 当low[v]==pre[v]时,表明不存在v到其祖先结点的连边,v构成强连通分量。若深搜树中存在一个叶子结点构成强连通分量,则通过以上判断可以求出,也即求出了第一个强连通分量; 若深搜树中所有叶子结点都不构成强连通分量,此时叶子结点必定与其父结点同在一个强连通分量中,可以将其缩到父结点中(具体操作是更新父结点的low数组),原来的父结点就变成了“大”叶子结点,还是通过low[v]=?pre[v]来判断这个“大”叶子结点是否构成强连通分量,一直这样下去,叶子结点将越来越大,直到求出第一个强连通分量为止。在求出第一个强连通分量后,我们将其包含的结点在原图中删除,问题又转化成了求第一个强连通分量的问题,理解还是一样的,以此类推,直到所有的强连通分量均被求出,此时算法结束。代码如下:#include<stdio.h> #include<string.h> #include<stack> #include<vector> using namespace std; #define MAXM 10010 vector<int>scc[MAXM]; stack<int>S; struct stu { int fo,to,ne; }; stu edge[MAXM*10]; int n,m,num; int head[MAXM]; int dfs_clock,scc_cnt; int low[MAXM]; int dfn[MAXM]; int sccno[MAXM]; int instack[MAXM]; void inin() { num=0; memset(head,-1,sizeof(head)); } void addedge(int a,int b) { stu E={a,b,head[a]}; edge[num]=E; head[a]=num++; } void tarjan(int u) { int v; low[u]=dfn[u]=++dfs_clock; S.push(u); instack[u]=true; for(int i=head[u];i!=-1;i=edge[i].ne) { v=edge[i].to; if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]);//为以后判断是否为子叶结点 } else if(instack[v])//在栈里,可以到达它的父节点 low[u]=min(low[u],dfn[v]); //将它和它的父节点合并成一个大节点 } if(low[u]==dfn[u]) { scc_cnt++; scc[scc_cnt].clear(); do { v=S.top(); S.pop(); sccno[v]=scc_cnt;//v属于第几个scc_cnt scc[scc_cnt].push_back(v); }while(v!=u); } } void slove(int n) { memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); memset(sccno,0,sizeof(sccno)); memset(instack,0,sizeof(sccno)); dfs_clock=scc_cnt=0;//初始化时间戳和SCC数目 for(int i=1;i<=n;i++) { if(!dfn[i])tarjan(i);//节点没有访问过 } } int main() { int a,b; while(scanf("%d%d",&n,&m),n|m) { inin(); while(m--) { scanf("%d%d",&a,&b); addedge(a,b); } slove(n);//此处从第一个节点开始 if(scc_cnt==1) printf("Yes\n"); else printf("No\n"); } return 0; }
相关文章推荐
- Keepalived+nginx+redis主从+tomcat一机多实例实现会话共享
- python用法笔记(数组(list、touple、dict)、字符串)
- 第四次作业--个人作业--必应词典案例分析
- notes reading word smart for the GRE 2nd Edition
- 05 代码片格式化
- document.body.clientHeight = 0 ??
- 49. PHP 页面静态化(2)
- cocos2d-x启动过程
- 流式大数据处理的三种框架:Storm,Spark和Samza
- 关于 微软必应词典客户端(pc) 的案例分析
- 数据库到底是干什么用的
- 如何在 Django 中使用 django-south, 实现数据迁移 (data migrations)
- HDOJ 1950 Bridging signals(LIS nlogn)
- hdoj 1034 Candy Sharing Game【基础题】&&【关键是理解题意】
- IOS开发——网络编程OC篇&Socket编程
- Excel中时间戳转换时间
- Vi操作备忘
- jQuery.extend 函数详解
- eclipse最实用快捷键
- 继电器模块典型电路图