zoukankan      html  css  js  c++  java
  • Popular Cows POJ

    //对于一个有向图,连通分量:对于分量中任意两点uv,
    //必然可以从u走到v,也可以从v走到u
    //强连通分量(scc):极大连通分量,也就是加上任何一个点之后,都不是连通分量
    
    //有向图通过缩点,转化为有向无环图(DAG),拓扑图
    //缩点是指将所有连通分量缩成一个点
    
    //Tarjan算法求scc
    //对每个点定义两个时间戳
    //dfn[u]表示遍历到u的时间戳
    //low[u]表示从u开始走,所能遍历到的最小时间戳
    //u是其所在强连通分量的最高点,等价于dfn[u]==low[u]
    
    //时间复杂度O(n+m)
    
    //缩点,先遍历所有点i
    //再遍历i的所有邻点
    //如果i和j不在同一个连通分量中,加一条新边,id[i]->id[j]
    //那么就把图建出来了,有向无环图DAG
    //按照拓扑序来做
    //连通分量编号递减的顺序一定是拓扑序
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int N = 10010, M = 50010;
    int n, m;
    int h[N], e[M], ne[M], idx;
    int dfn[N], low[N], timestamp;
    //          栈顶
    int stk[N], top;
    bool in_stk[N];
    //属于哪个连通分量
    int id[N];
    //当前有多少强连通分量
    int scc_cnt;
    //每个强连通分量中点的数量
    int Size[N];
    //每个连通分量的出度
    int dout[N];
    void add(int a, int b)
    {
    	e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
    }
    void tarjan(int u)
    {
    	//先都等于时间戳
    	dfn[u] = low[u] = ++ timestamp;
    	//把当前点加到栈当中去
    	//栈当中存的值,不单单是当前路径上的值,还可能存其他边上的点
    	//存的点,都不是它所在强连通分量的最高点
    	//都是,当前,还没有搜完的遍历完的,强连通分量的所有点
    	stk[ ++ top] = u;
    	//记录是否在栈当中
    	in_stk[u] = true;
    	//遍历u的所有临点
    	for (int i = h[u]; i != -1; i = ne[i])
    	{
    		int j = e[i];
    		//如果还没有被遍历过
    		if (!dfn[j])
    		{
    			//遍历
    			tarjan(j);
    			//更新
    			low[u] = min(low[u], low[j]);
    		}
    		//否则,说明遍历过,而且还在栈当中
    		else if (in_stk[j])
    			//取最小
    			low[u] = min(low[u], dfn[j]);
    	}
    	//遍历完u之后,发现u能到的最前面的点就是自己了
    	if (dfn[u] == low[u])
    	{
    		//那就说明,u肯定是所在强连通分量的最高点
    		//所有强连通分量个数++
    		++ scc_cnt;
    		int y;
    		do
    		{
    			//先取出栈顶元素
    			y = stk[top -- ];
    			//表示出栈
    			in_stk[y] = false;
    			//标记当前点属于哪个强连通分量
    			id[y] = scc_cnt;
    			//
    			Size[scc_cnt] ++ ;
    			//y==u时,表示所在强连通分量处理完了
    		}
    		while (y != u);
    	}
    }
    int main()
    {
    	scanf("%d%d", &n, &m);
    	memset(h, -1, sizeof h);
    	while (m -- )
    	{
    		int a, b;
    		scanf("%d%d", &a, &b);
    		add(a, b);
    	}
    	//建新图
    	for (int i = 1; i <= n; i ++ )
    		if (!dfn[i])
    			tarjan(i);
    	//统计所有点的出度
    	//遍历原图的所有边
    	for (int i = 1; i <= n; i ++ )
    		for (int j = h[i]; ~j; j = ne[j])
    		{
    			int k = e[j];
    			int a = id[i], b = id[k];
    			//从a走到b
    			//所有a的出度增加
    			if (a != b)
    				dout[a] ++ ;
    		}
    	int zeros = 0, sum = 0;
    	for (int i = 1; i <= scc_cnt; i ++ )
    		if (!dout[i])
    		{
    			//统计有多少个点的出度为0
    			//如果只有一个,那么最终答案就是出度为0的点的size
    			//如果大于一个,那么出度为0的强连通块必然不能互相到达
    			//所有就是0
    			zeros ++ ;
    			sum += Size[i];
    			if (zeros > 1)
    			{
    				sum = 0;
    				break;
    			}
    		}
    	printf("%d
    ", sum);
    	return 0;
    }
    
    
  • 相关阅读:
    IDEA 配置
    从一个多项目Web工程看Eclipse如何导入Gradle项目
    mac java环境变量配置
    gradle多项目构建及依赖
    gradle新建工程,多项目依赖,聚合工程
    JSTL的一些使用规范,坑
    mysql主从配置
    windows系统常用软件及配置介绍
    mac必装工具以及mac使用介绍
    一种480 MHz无线数传模块的设计
  • 原文地址:https://www.cnblogs.com/QingyuYYYYY/p/12851939.html
Copyright © 2011-2022 走看看