zoukankan      html  css  js  c++  java
  • tarjan求缩点--P2746 [USACO5.3]校园网Network of Schools

    一些概念:

    1. 强连通 :在有向图(G)中,两个顶点 (u) ,(v),如果存在一条路径 (u)(v) 且存在一条路径(v)(u),则这两个顶点是强连通的

    2. 强连通图 :如果有向图(G)中的每两个顶点都强连通,那么称(G)是一个强连通图

    3. 极大强连通子图(G)是一个极大强连通子图,当且仅当(G)是一个强连通图,且其中不存在其他强连通图

    4. 强连通分量 :有向非强连通图的极大强连通子图称为强连通分量

    结论 :若将有向图中所有的强连通分量缩成一个点,则原图会形成一个(DAG)(有向无环图)

    Tarjan算法:

    比较重要的两个数组的定义

    (dfn_u)为结点(u)搜索的次序编号(时间戳)不会改变,(low_u)(u)(u)的子树(经过最多一条后向边或栈中横叉边)能够回溯到的最早的栈中结点的次序号

    则当结点(u)的搜索过程结束后,若(dfn_u = low_u),则以(u)为根的搜索子树上所有还在栈中的结点是一个强连通分量。

    算法流程

    1. 因为(tarjan)图不一定联通,所以我们每次从时间戳未更新的点开始(dfs)

    2. 链前按顺序将点入栈,搜到一个强连通分量出栈,直到恰好能回溯到它本身的点出栈为止

    3. 对每一个强连通分量染色,重新建图,变成了一个(DAG)

    例题

    这道题对于任务(A),求必须接受新软件副本的最少学校数目(子任务 A),两两联通的学校只需要选择一个就可以了,所以两两联通的学校可以合并,由此想到了缩点,缩点之后形成了(DAG),于是任务(A)我们要求的就是缩点后入度为0的点,因为他无法通过其他强连通分量到达

    对于任务(B),计算最少需要增加几个扩展,使得不论我们给哪个学校发送新软件,它都会到达其余所有的学校,也就是把一个非强连通图变成一个强连通图,也就是缩点后入度为0和出度为0的块取个max

    code

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    using namespace std;
    int read(){
    	int x = 1,a = 0;char ch = getchar();
    	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
    	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
    	return x*a;
    }
    const int maxn = 1e5+10;
    int n;
    struct node{
    	int to,next;
    }ed[maxn];
    int head[maxn],tot;
    void add(int u,int to){
    	ed[++tot].to = to;
    	ed[tot].next = head[u];
    	head[u] = tot;
    }
    int dfn[maxn],low[maxn],st[maxn],top,cnt,color,col[maxn];
    void tarjan(int x){
    	dfn[x] = low[x] = ++cnt;
    	st[++top] = x;
    	for (int i = head[x];i;i = ed[i].next){
    		int to = ed[i].to;
    		if (!dfn[to]){
    			tarjan(to);
    			low[x] = min(low[x],low[to]);
    		}
    		else if (!col[to]) low[x] = min(low[x],dfn[to]);
    	}
    	if (dfn[x] == low[x]){
    		color++;
    		int y;
    		while (y = st[top--]){
    			col[y] = color;
    			if (x == y) break;
    		}
    	}
    }
    int maxx,minn;
    int in[maxn],out[maxn];
    int main(){
    	n = read();
    	for (int i = 1;i <= n;i++){
    		int x;
    		while (~scanf ("%d",&x)&&x != 0) add(i,x);
    	}
    	for (int i = 1;i <= n;i++){
    		if (!dfn[i]) tarjan(i);
    	}
    	if (color == 1){printf("1
    0
    ");return 0;}
    	for (int i = 1;i <= n;i++){
    		for (int j = head[i];j;j = ed[j].next){
    			int to = ed[j].to;
    			if (col[to] != col[i]) in[col[to]]++,out[col[i]]++;
    		}
    	}
    	for (int i = 1;i <= color;i++){
    		if (in[i] == 0) maxx++;
    		if (out[i] == 0) minn++;
    	}
    	printf("%d
    ",maxx);
    	printf("%d
    ",max(maxx,minn));
    	return 0;
    }
    
  • 相关阅读:
    redis 数据库总结
    drf 序列化类总结
    drf 视图类经典总结
    celery 简介
    虚拟环境搭建pip换源
    git 与 svn,简介差别
    redis 数据库简介
    auth 模块
    python的注释与用户交互 基本数据类型
    python入门
  • 原文地址:https://www.cnblogs.com/little-uu/p/13950227.html
Copyright © 2011-2022 走看看