//自己理解着又敲了一遍代码,发现就是那回事,我希望自己能写写题解,一方便自己看,二方便别人理解 //但诚挚地说,我现在还不明白连通图,只是跟着学长思路走了一遍,至于如果我自己第一次看到这个题,我是不会想到连通图的 #include<iostream> #include<algorithm> #include<string.h> #include<stdio.h> #include<vector>//头文件,之后用来申请数组,这一点可能我理解的不清楚,只会比葫芦画瓢 using namespace std; #define min(a, b) a<b?a:b #define N 10010 vector<vector<int> >G;//学长说这个比下面那一行的方式好,可我也不知道为什么 //vector<int>G[N]; int n, m; int visit[N], rode[N];//visit用来表示访问的第几个数,rode用来存最短的距离 int Stack[N];//自己建立栈 bool Instack[N];// 判断是否在栈里面 int Time, top; int num, cnt;//强连通图:(大家可以百度)是有向图,且对于图中任意两个不同的顶点i和j,都存在从i到j的路径,则称是强连通图;强连通图只有一个强连通分量。 //故若判断小希是否都能到达所有房间,就是判断这个图是不是强连通图。也就是判断cnt是否等于1,且cnt=1时num是否等于n;cnt==1&&num==n代表图有一个连通分量且是他自身。即这是强连通图; //若不是强连通图,这道题是有向图,则称为“非强连通的有向图” 而它的连通分量可能有若干个 void Init()//初始化,在需要很多处是滑的时候最好写在一个专有的函数里,方便检查Init initially(初始化) { G.clear();//清空 G.resize(n+1);//申请内存 //忘记申请内存了(这是我自己打的时候出现的错误) memset(visit, 0, sizeof(visit)); memset(rode, 0, sizeof(rode)); memset(Stack, 0, sizeof(Stack)); memset(Instack, false, sizeof(Instack)); num=cnt=Time=top=0; } void Tarjan(int u) { visit[u]=rode[u]=++Time;//visit和rode从1开始 Stack[top++]=u;//入栈 Instack[u]=true;//表明在栈里面了 int len=G[u].size();//看有多少在那个里面 //这里写错了(这是我自己打的时候出现的错误) int v; for(int i=0; i<len; i++) { v=G[u][i];// 数组里面的第几个 //看来对那个不熟悉(这是我自己打的时候出现的错误) if(!visit[v])//其实也可以这样写if(!rode[v]),无非就是判断这个点是否访问过,没有访问过时visit[v] rode[v]都为0; { Tarjan(v);//递归,这种算法叫做Tarjan,你们可以上网搜搜加深理解 rode[u]=min(rode[u], rode[v]);//当回朔时,就更改u也就是父亲的最短路径,因为儿子v的是最先更新的; //原因如下:因为递归到最后如果有“连通块”就会执行 else if 例如1->2->3->1;当执行到3->1时就会因为1已经在栈里面,而执行else if } else if(Instack[v]) { rode[u]=min(rode[u], visit[v]);//其实写成rode[u]=min(rode[u], rode[v]);也是可以的,学长说当求割点时必须这样写; } } if(rode[u]==visit[u]) { do { num++; v=Stack[--top]; Instack[v]=false; }while(u!=v);//do while 会更简单吧,第一次用 do while cnt++; } } void solve() { Tarjan(1); if(cnt==1&&num==n) puts("Yes"); else puts("No"); } int main() { while(scanf("%d%d", &n, &m), n+m) { Init(); int a, b; while(m--) { scanf("%d%d", &a, &b); G[a].push_back(b); } solve(); } return 0; }
时间: 2024-11-09 00:04:52