ccf 高速公路

来源:互联网 发布:淘宝发布产品找不到 编辑:程序博客网 时间:2024/04/29 21:31
顺便用刚理解的 tarjain 算法 的模板 套了一发 ,结果直接是 100 ,一开始 想不到 高效算法,用 floyd-warshall 算法 o(n^3 )算法,想着可以骗点分,额,只骗了40 分,就超时了,果然时间复杂度够可以的。


据说 tarjan 是 SCC 的第一个线性算法。每个点只入一次栈,每个点只会遍历一次。确实很高效。


再说一个对这个算法的理解:


我觉得在这个算法中,一个点是否是一个强联通分量的最先被访问的点,取决于主循环的访问顺序,在一个强联通分量,任意两个点之间都可以互相到达,从一个强联通分量中的任意一个点神搜下去,肯定可以到达所有这个强联通分量中的点,只是每个点的pre 值不同,也就是被访问到的顺序不同(取决于这个点的编号,编号小的)。 从一个点 深搜,访问到的点的pre 值比当前小,说明这个点不是 这个联通分量的最先被访问的点。(一个联通分量总有一个点会被先访问),从这个点访问下去,就能将一个联通分量中的所有点都访问到,当这个深搜退出时,

#include <iostream>#include <stack>#include <string>#include <algorithm>#include <vector>#include <string.h>using namespace std;const int maxn=10001; // ----- 视程序给出的数据范围而定vector<int> G[maxn];//邻接表int pre[maxn],low[maxn],scc_cnt, dfs_cnt; //int num=0;// 用来记录便利城市对数int in_stack[maxn];//这个数组表示一个点是否还在stack 中,如果还在那么说明这个点还不属于任意一个联通分量stack<int> s;void dfs(int u){pre[u]=low[u]=++dfs_cnt;s.push(u);for(int i=0;i<G[u].size();i++){    int v =G[u][i];    if(!pre[v]){        dfs(v); // 接下去深搜,返回的时候,这个点就有明确的low 值了,代表从这个点深搜下去能够达到的 最先被访问的点        low[u]=min(low[u],low[v]);    }   // pre[i]初始化设为0 ,代表这个点没有被访问过    else if(!in_stack[v]){ //如果这个点还被访问过,而且这个点还在栈中,那么这个点的pre 值很可能比 当前点的low值小。        low[u]=min(low[u],pre[v]);    }}if(low[u]==pre[u]){ // 说明这个点就是一个强联通分量的最先被访问的点scc_cnt++;int  single=0; // 用来记录当前联通分量的便利城市对数。for(;;){// 从当前栈弹出这个联通分量的所有点    int x=s.top();    s.pop();     single++;   in_stack[x]= scc_cnt;   if(u==x)break;  // 直到 x==u 退出}num+=((single-1)*single)/2;}}int main(){int n,m;dfs_cnt=scc_cnt=0;memset(pre,0,sizeof(pre));memset(low,0,sizeof(low));int start,endx;cin>>n>>m;for(int i=0;i<m;i++){ cin>>start>>endx;G[start].push_back(endx);}for(int i=1;i<=n;i++){    if(!pre[i])dfs(i);}cout<<num<<endl;return 0;}

主循环中下次深搜的就是另一个联通分量的最先被访问到的点了。







0 0
原创粉丝点击