题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1269
3 3 1 2 2 3 3 1 3 3 1 2 2 3 3 2 0 0
Yes No
两种算法效率差不多;
tarjin算法AC代码:
#include<iostream> #include<cstring> #include<cstdio> using namespace std; const int MAXN=100005; int n,m,u,v; int tot,top,cnt,flag; struct node { int v; // 下个顶点; int next; }edge[MAXN]; // 保存每条边; int pre[MAXN],Stack[MAXN],DFN[MAXN],Low[MAXN],Belong[MAXN]; int instack[MAXN/10+10]; // 记录是否在栈内; void init() { tot=cnt=top=flag=0; memset(pre,-1,sizeof(pre)); memset(instack,0,sizeof(instack)); memset(DFN,0,sizeof(DFN)); } // 记录的是u-->v的这条边,编号为tot; void addEdge(int u,int v) // 邻接表建图 { edge[tot].v=v; edge[tot].next=pre[u]; pre[u]=tot++; } void Tarjin(int v) { DFN[v]=Low[v]=++flag; // 将进入的时间戳从tot开始; instack[v]=1; // 标记顶点v已经如栈; Stack[top++]=v; // 入栈; for(int e=pre[v];e!=-1;e=edge[e].next){ // 相邻的边的枚举; int j=edge[e].v; // 下一个顶点; if(!DFN[j]){ Tarjin(j); Low[v]=min(Low[v],Low[j]); }else if(instack[j]){ // 还在栈内; Low[v]=min(Low[v],DFN[j]); // 最小出现的时间; } } // v是该强连通分量的根; if(DFN[v]==Low[v]){ cnt++; // 计数加一; int t; do{ t=Stack[--top]; // 将栈栈顶元素抛出; instack[t]=0; // 并标记为0; Belong[t]=cnt; // 标记t属于分量cnt; }while(t!=v); // 直到该分量全部出栈; } } void solve() { for(int i=1;i<=n;i++){ if(!DFN[i]) // 对每个没有遍历的点遍历; Tarjin(i); } } int main() { cin.sync_with_stdio(false); while(cin>>n>>m&&(n||m)){ // 不能写成了(n||m); init(); for(int i=0;i<m;i++){ cin>>u>>v; addEdge(u,v); // 添加一条u->v的边; } solve(); if(cnt==1) cout<<"Yes"<<endl; else cout<<"No"<<endl; } return 0; <p>}</p>
双向DFS AC代码:(挑战程序设计竞赛中有详解)
#include<iostream> #include<queue> #include<vector> #include<cstring> using namespace std; const int MAXN=10010; int n,m; int cnt; vector<int>G[MAXN]; vector<int>rG[MAXN]; vector<int>vs; bool vis[MAXN]; int cmp[MAXN]; void add_edge(int u,int v) { G[u].push_back(v); // 正向建图 rG[v].push_back(u); // 反向建图 } void init() { cnt=0; memset(vis,0,sizeof(vis)); vs.clear(); for(int i=0;i<MAXN;i++){ rG[i].clear(); G[i].clear(); } } void dfs(int v) { vis[v]=true; for(int i=0;i<G[v].size();i++){ if(!vis[G[v][i]]) dfs(G[v][i]); } vs.push_back(v);<span style="white-space:pre"> // 将顶点v放到vs栈中;理解这个顺序很重要</span> } void rdfs(int v,int cnt) { vis[v]=true; cmp[v]=cnt; // 记录每一个顶点属于哪个强连通分量; for(int i=0;i<rG[v].size();i++){ if(!vis[rG[v][i]]) rdfs(rG[v][i],cnt); } } void SCC() // Strong Connected Component { for(int v=1;v<=n;v++){ // 正向dfs if(!vis[v]) dfs(v); } memset(vis,0,sizeof(vis)); // 注意,vis要再次清空; for(int i=vs.size()-1;i>=0;i--){ // 逆向dfs; if(!vis[vs[i]]) rdfs(vs[i],cnt++); } } int main() { int u,v; cin.sync_with_stdio(false); while(cin>>n>>m&&(n||m)){ init(); for(int i=1;i<=m;i++){ cin>>u>>v; add_edge(u,v); // 双向建图; } SCC(); if(cnt==1) cout<<"Yes"<<endl; else cout<<"No"<<endl; } return 0; }