zoukankan      html  css  js  c++  java
  • POJ 2553 The Bottom of a Graph TarJan算法题解

    本题分两步:

    1 使用Tarjan算法求全部最大子强连通图。而且标志出来

    2 然后遍历这些节点看是否有出射的边,没有的顶点所在的子强连通图的全部点,都是解集。

    Tarjan算法就是模板算法了。

    这里使用一个数组和一个标识号,就能够记录这个顶点是属于哪个子强连通图的了。

    然后使用DFS递归搜索全部点及其边,假设有边的还有一个顶点不属于本子强连通图。那么就说明有出射的边。

    有难度的题目:


    #include <stdio.h>
    #include <stdlib.h>
    #include <vector>
    #include <stack>
    using namespace std;
    
    const int MAX_VEC = 5005;
    int n, m;
    vector<int> gra[MAX_VEC];
    stack<int> stk;
    int dfn[MAX_VEC];//tarjan算法记录深度探索得到的标号
    int low[MAX_VEC];//tarjan算法记录回溯得到的最低顶点编号
    bool inStk[MAX_VEC];//记录是否在栈里面,也能够记录是否被訪问过了
    int connectGrp[MAX_VEC];//标志所属的连通子图标号 == connectNum
    int vecNum;//顶点标号
    int connectNum;//最大强连通子图标号
    int out[MAX_VEC];//出度记录
    
    void tarjan(int u)
    {
    	dfn[u] = low[u] = vecNum++;
    	stk.push(u);
    	inStk[u] = 1;
    	for (int i = 0; i < (int)gra[u].size(); i++)
    	{
    		int v = gra[u][i];
    		if (!dfn[v])
    		{
    			tarjan(v);
    			low[u] = min(low[u], low[v]);//两处的不同,和含义不同
    		}
    		else if (inStk[v]) low[u] = min(dfn[v], low[u]);//两处的不同
    	}
    	if (low[u] == dfn[u])
    	{
    		++connectNum;
    		while (stk.size())
    		{
    			int v = stk.top(); stk.pop();
    			inStk[v] = false;
    			connectGrp[v] = connectNum;
    			if (u == v) return;
    		}
    	}
    }
    
    void solveConnect()
    {
    	memset(dfn, 0, sizeof(dfn));
    	memset(low, 0, sizeof(low));
    	memset(inStk, 0, sizeof(inStk));
    	for (int i = 1; i <= n; i++)
    	{
    		if (!dfn[i]) tarjan(i);
    	}
    }
    
    void dfsCountOut(int u)
    {
    	inStk[u] = true;	//记录是否被訪问过了
    	for (int i = 0; i < int(gra[u].size()); i++)
    	{
    		int v = gra[u][i];
    		if (connectGrp[u] != connectGrp[v])
    		{
    			out[connectGrp[u]]++;//这个组的出度添加; connectGrp[] == connectNum
    		}
    		if (!inStk[v]) dfsCountOut(v);//深度优先,不须要使用额外空间
    	}
    }
    
    void countConnectOut()
    {
    	memset(inStk, 0, sizeof(inStk));	//这里仅仅记录是否被訪问过的了。
    	memset(out, 0, sizeof(out));
    	for (int i = 1; i <= n; i++)
    	{
    		if (!inStk[i]) dfsCountOut(i);
    	}
    }
    
    int main()
    {
    	int u, v;
    	while (scanf("%d", &n) && n)
    	{
    		connectNum = 0;
    		vecNum = 1;
    		scanf("%d", &m);
    		for (int i = 1; i <= n; i++)
    			gra[i].clear();
    		while (stk.size()) stk.pop();	//清零。重中之重
    
    		for (int i = 0; i < m; i++)
    		{
    			scanf("%d %d", &u, &v);
    			gra[u].push_back(v);	//有向图
    		}
    
    		solveConnect();
    		countConnectOut();
    
    		for(int i = 1; i <= n; ++i)
    			if(out[connectGrp[i]] == 0)
    				printf("%d ", i);
    		putchar('
    ');
    	}
    	return 0;
    }
    
    
    



  • 相关阅读:
    PHP atanh() 函数
    PHP atan2() 函数
    PHP atan() 函数
    #检查磁盘使用率超过90%,并且后台进程没有rman在跑,就运行 /data/script/del_dg_arch.sh 脚本清理归档
    [学习笔记]自适应辛普森积分
    C# 如何写 DEBUG 输出
    C# 如何写出一个不能被其他程序集继承的抽象类
    C# 如何写出一个不能被其他程序集继承的抽象类
    C# 如何引用 WshShell 类
    C# 如何引用 WshShell 类
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/5306077.html
Copyright © 2011-2022 走看看