【图论】tarjan
刚接触tarjan,tarjan其实更多是用来找强联通分量。我这里呢,是看qsc的视频学的。卿学姐讲的其实很清楚啦。
我这里只是做个整理。
low[]:表示能到达这个点的最小编号。[树枝边]。啊,其实我觉得就是保存环路的起点。QWQ。因为只要记录了这个点,栈中经历的点都能到达嘛都是强联通啊。
dfn[]:搜索到这个点的时间是多少。[后叉边]。时间戳。
vis[]:该点是否进栈。
伪代码如下:
1 tarjan(x) 2 low[x] = dfn[x] = ++index; 3 stack.push(x); 4 vis[x] = 1;//表示在栈中。 5 for(v属于E){ 6 if(vis[v] == 0) 7 tarjan(v),low[x] = min(low[x],low[v]); 8 if(vis[v]){ 9 low[x] = min(low[x],dfn[v]); 10 } 11 } 12 13 if(low[x] == dfn[x]){ //这里其实就是表示点已经通过某一种方式到达了自己 14 做出栈操作 15 }
这里主要就是在两个if中要理解。
如果该点没有进栈,当前存的当然要是最短的。
如果该点进栈了,就去看他的dfn[]存的是什么,就更新。
具体的举例请看卿学姐的视频啊。QWQ。。有时间我补图上来。。
例题题目链接:NOIP2015信息传递
代码:
#include<iostream> #include<algorithm> #include<vector> #include<queue> #include<stack> using namespace std; const int maxn = 2e5+7; vector<int> E[maxn]; int dfn[maxn],low[maxn],tot = 0,n,ans = maxn; stack<int> S; int vis[maxn]; void tarjan(int x){ low[x] = dfn[x] = tot++; S.push(x); vis[x] = 1; for(int i = 0 ; i < E[x].size() ;i++){ int v = E[x][i]; if(!dfn[v]){ tarjan(v); low[x] = min(low[x],low[v]); } else if(vis[v]){ low[x] = min(low[x],dfn[v]); } } if(low[x] == dfn[x]){ int cnt = 0; while(1){ int now = S.top(); S.pop(); vis[x] = 0; cnt++; if(now == x){ break; } } if(cnt > 1 ) ans = min(ans,cnt); } } int main(){ cin>>n; for(int i = 1; i <= n ;i++){ int x; cin>>x; E[i].push_back(x); } for(int i = 1; i <= n ;i++){ if(!dfn[i]){ tarjan(i); } } cout<<ans<<endl; return 0; }