设为首页 加入收藏

TOP

ZOJ_3795 Grouping(强连通分量 拓扑)
2015-11-21 00:58:56 来源: 作者: 【 】 浏览:1
Tags:ZOJ_3795 Grouping 连通 分量 拓扑


题解:
这是我的第一道强连通分量,虽然参考了别人的代码,还是很有收获。强连通分量的查找和处理是很多图论题目的第一步,所以还是很重要,这道题只是有向图的强连通处理。
这道题因为题目有讲每组关系都是不小于,那么如果出现环的话那只有一种情况,就是环上所有人都是相等的年龄,则这个环上所有的人的比较关系都是可以等价的,这就是为什么我们要先对强连通分量尽行缩点处理,将每一个强连通分量作为一个整体,之后再依据层次关系构造拓扑序,利用拓扑序找到最长的一条序列。
算法讲解
代码参考
测试数据:

4 4
1 2
2 3
3 4
4 1

5 3
1 2
2 3
3 4

9 9
5 4
4 1
1 3
3 2
2 1
1 6
6 7
7 8
8 9

5 5
1 2
2 3
3 4
4 1
5 1

代码实现:

#include 
   
     #include 
    
      #include 
     
       #include 
      
        #include 
       
         #include 
        
          #define MAX_N 100010 #define MAX_M 300010 using namespace std; struct E{ int from,to,next; bool sign; }; int N,M; int result; int edgenum;//边的数目 int top,cnt,Time;//栈顶,强联通分量数目,时间轴 E edge[MAX_M];//边集 int dis[MAX_N];//拓扑序中节点的权值 int head[MAX_N];//前向星存储head数组 int belong[MAX_N];//记录某个点属于哪一个强连通分量 int indegree[MAX_N];//记录拓扑序中强连通分量入度 bool instack[MAX_N];//节点是否入栈 int DFN[MAX_N];//节点u搜索的次序编号(时间戳) int Low[MAX_N];//节点u或u的子树能追溯到的最早的栈中节点的次序号 int Stack[MAX_N];//手写栈 vector
         
           G[MAX_N];//存储拓扑序 vector
          
            bcnt[MAX_N];//存储每一个强连通分量 void add(int u,int v);//前向星加边 void tarjan(int u);//查找强连通分量 void tarjan_ini(int all); void suodian();//缩点函数,按照每个强连通分量构造拓扑序 int bfs(); int main(){ //freopen("in.txt","r",stdin); while( scanf("%d%d",&N,&M) != EOF ){ int a,b; edgenum = 0; memset(head,-1,sizeof(head)); while( M-- ){ scanf("%d%d",&a,&b); add(a,b); } tarjan_ini(N); suodian(); result = bfs(); printf("%d\n",result); } return 0; } void tarjan_ini(int all){ memset(DFN,-1,sizeof(DFN)); //初始化三个指针 top = Time = cnt = 0; for( int i = 1; i <= all; i++ ){ //查找每一个未被遍历的点的强连通分量 if( DFN[i] == -1 ){ tarjan(i); } } } //递归查找强连通分量 void tarjan(int u){ //标记时间戳 DFN[u] = Low[u] = ++Time; //入栈,注意这里的前++ Stack[++top] = u; instack[u] = true; //查找所有与u相邻边,寻找强连通分量 for( int i = head[u]; i != -1; i = edge[i].next ){ E tmp = edge[i]; //未被访问节点 if( DFN[tmp.to] == -1 ){ tarjan(tmp.to); //u或u的子树所能追溯到的最早的栈中节点 Low[u] = min(Low[u],Low[tmp.to]); } //已被访问且仍在栈中节点 else if( instack[tmp.to] ){ Low[u] = min(Low[u],DFN[tmp.to]); } } //u为强连通分量节点,弹栈,所有元素存储到一个vector数组 if( DFN[u] == Low[u] ){ int now; cnt++; bcnt[cnt].clear(); do{ //弹栈,注意这里的后-- now = Stack[top--]; //标记属于哪一个强连通分量 belong[now] = cnt; instack[now] = false; bcnt[cnt].push_back(now); }while( now != u );//弹到根节点截止 } } void suodian(){ for( int i = 1; i <= cnt; i++ ){ G[i].clear(); indegree[i] = 0; } //利用所有不在某一个强连通分量内部的边构造拓扑序 for( int i = 0; i < edgenum; i++ ){ int u,v; u = belong[edge[i].from]; v = belong[edge[i].to]; if( u != v ){ indegree[v]++; G[u].push_back(v); } } } void add(int u,int v){ E tmp = {u,v,head[u],false}; edge[edgenum] = tmp; head[u] = edgenum++; } int bfs(){ int tmp; queue
           
             Q; for( int i = 1; i <= cnt; i++ ){ if( indegree[i] == 0 ){ Q.push(i); //初始权值为强连通分量的size dis[i] = bcnt[i].size(); } else{ dis[i] = 0; } } while( !Q.empty() ){ int u = Q.front(); Q.pop(); int len = G[u].size(); for( int i = 0; i < len; i++ ){ int v = G[u][i]; //叠加找到最大权值 dis[v] = max(dis[v],(int)bcnt[v].size()+dis[u]); //若该图为一个强连通图,不会进入这里更新 //tmp = max(tmp,dis[v]); indegree[v]--; if( indegree[v] == 0 ){ Q.push(v); } } } //得到结果 tmp = 1; for( int i = 1; i <= cnt; i++ ){ tmp = max(tmp,dis[i]); } return tmp; } 
           
          
         
        
       
      
     
    
   
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇C++的内置类型和用户自定义类型的.. 下一篇List.contains(Object object)方法

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: