为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A房间和B房间,只说明可以通过这个通道由A房间到达B房间,但并不说明通过它可以由B房间到达A房间。Gardon需要请你写个程序确认一下是否任意两个房间都是相互连通的,即:对于任意的i和j,至少存在一条路径可以从房间i到房间j,也存在一条路径可以从房间j到房间i。 Input
3 3 1 2 2 3 3 1 3 3 1 2 2 3 3 2 0 0
Yes No
在有向图中,如果两个顶点vi,vj间有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。 如果有向图的每两个顶点都强连通,则称该有向图是一个强连通图。非强连通的有向图的极大强连通子图,称为强连通分量
Kosaraju算法是求解有向图强连通分量(strongconnected component)的三个著名算法之一,能在线性时间求解出一个图的强分量。用Sedgewick爷爷的话说,就是“现代算法设计的胜利”。
题意 判断是不是强连通图
思路:如果这个图是强连通的,那么从任意一个顶点s开始进行DFS都可以访问到图中的所有点,如果不能访问到所有点那么就说明这个图肯定不是强连通图,但能访问到所有点也并不能说明此图为强连通,只有所有点同时也能访问到这个点才说明此图为强连通图。这就可以利用kosaraju的思想,将图进行逆置,然后在从s开始DFS,此时若能访问到所有点则说明未逆置的图中所有点都有到达s的路径,综上此图就为强连通图。若以上两个条件(在G中从s开始能访问到所有顶点,在G转置中从s开始也能访问到所有顶点)有一个不满足就不是强连通图。当然这个点从1..N中随便选择一个就行,我的代码选择的1.。提交虽然最坏情况下是两遍DFS,但是由于我将DFS非递归化,所以时间也就是
O(V + E),常数也不大,2左右吧。
#include<iostream> #include<stack> #include<cstdio> #include<cstring> using namespace std; struct node{int v,next;}; node edge1[10100],edge2[10100]; int head1[10100],head2[10100],cnt,n,m; void add1(int x,int y) { edge1[cnt].v=y; edge1[cnt].next=head1[x]; head1[x]=cnt++; } void add2(int x,int y) { edge2[cnt].v=y; edge2[cnt].next=head2[x]; head2[x]=cnt++; } bool dfs(int u,node *edge,int *head) { stack<int>it; it.push(u);int vis[10100]={0};vis[u]=1; cnt=0; while(!it.empty()) { int x=it.top();it.pop();cnt++; for(int i=head[x];i!=-1;i=edge[i].next) { int v=edge[i].v; if(!vis[v]) { it.push(v);vis[v]=1; } } } if(cnt==n)return true; return false; } bool kosaraju() { if(!dfs(1,edge1,head1))return false; if(!dfs(1,edge2,head2))return false; return true; } int main() { while(scanf("%d%d",&n,&m)&&n) { cnt=0; memset(head1,-1,sizeof(head1)); memset(head2,-1,sizeof(head2)); for(int i=0;i<m;i++) { int a,b;scanf("%d%d",&a,&b); add1(a,b); add2(b,a); } if(kosaraju())printf("Yes\n"); else printf("No\n"); } }
虽然是根据上面的思想去写的代码,但不知道为什么超时了,不知道是不是没有用vector来实现邻接表的原因
,求大神指教
下面是用vector的正确代码d
#include<iostream> #include<stack> #include<cstdio> #include<vector> #include<cstring> using namespace std; vector<int>s1[10100],s2[10100]; int cnt,n,m;bool dfs(int u,vector<int>*s) { stack<int>it; it.push(u);int vis[10100]={0};vis[u]=1; cnt=0; while(!it.empty()) { int x=it.top();it.pop();cnt++; for(int j=0;j<s[x].size();j++) { int v=s[x][j]; if(!vis[v]) { it.push(v);vis[v]=1; } } } if(cnt==n)return true; return false; } void trans() { for(int i=1;i<=n;i++) { for(int j=0;j<s1[i].size();j++) { s2[s1[i][j]].push_back(i); } } } bool kosaraju() { if(!dfs(1,s1))return false; trans(); if(!dfs(1,s2))return false; return true; } int main() {while(scanf("%d%d",&n,&m)&&n) { cnt=0;for(int i=0;i<m;i++) { int a,b;scanf("%d%d",&a,&b); s1[a].push_back(b); } if(kosaraju())printf("Yes\n"); else printf("No\n"); for(int i=1;i<=n;i++) { s1[i].clear(); s2[i].clear(); } }}下面是一种更简介的方法,直接从每一点出发判断是否能到达剩下的所有的点,简单暴力#include<iostream> #include<vector> using namespace std; #define MAX 10005 vector<int>v[MAX]; //用vector创建邻接表 bool flag[MAX]; void DFS(int k) //深度优先搜索 { int i; flag[k]=true; for(i=0;i<v[k].size();i++) if(flag[v[k][i]]==false) DFS(v[k][i]); } int main() { int M,N; int a,b; int i,j; bool count; while(cin>>N>>M&&M||N) { count=true; for(i=0;i<M;i++) //邻接表的建立 { cin>>a>>b; v[a].push_back(b); } for(i=1;i<=N;i++) //以每个房间为顶点深度优先搜索 { for(j=1;j<=N;j++) flag[j]=false; DFS(i); for(j=1;j<=N;j++) if(flag[j]==false) { count=false; break; } if(count==false) break; } if(count) cout<<"Yes"<<endl; else cout<<"No"<<endl; for(i=1;i<=N;i++) //清空邻接表 v[i].clear(); } return 0; }