zoukankan      html  css  js  c++  java
  • 【训练题】强连通分量缩点 P1679

    Description

    有 N 个人和每个人所认识人的列表,注意:即使B在A的列表中,A也不一定在B的列表中。现在小明有一个重要消息要通知这N个人,注意:如果A认识B,则当A得到这个消息,他就会立即通知B。


    现在请你完成下面两个任务:


    任务1:请你计算要让N个人都得到消息,那么小明必须把这个消息直接通知的人的最少数目。


    任务2:如果小明想要只告诉这N个人中的任何一个人,其他所有人都能得到消息,那么可能需要在某些人的认识列表添加认识的新成员。请你计算,最少添加多少新成员,就可以让任何一个人得到消息,都能传到其他所有人。


    Input

    第一行包括一个整数 N:表示人数,这些人编号依次为1..N。接下来 N 行中每行都表示一个认识关系的列表,第 i+1 行包括表示第 i 人认识的人的列表,每个列表用 0 结束,空列表只用一个 0 表示。


    Output

    第一行包括一个正整数:任务 1 的解。第二行应该包括任务2 的解。


    Hint

    N<=10000


    Solution

    首先这道题要用tarjan缩点成一个DAG图,之后统计入度和出度为1的点,这些点就需要一条边来形成环。边的数量取入度和出度为1的点的数量的最大值就可以了因为加1条边可以分别处理一个入度为1和出度为1的点,单独的点就需要单独一条边来处理。tarjan找强连通分量的原理是把点压进栈里进行dfs,对dfn值为0的点进行tarjan更新dfn这个时间戳,把low设置为u和v里面low值最小的,然后当v还没有属于任何一个强连通分量的时候,把s的low值更新成为low[u]和dfn[v]里的最小值。如果他们属于同一个连通分量他们的low值都是祖先结点的low值。这个过程类似于动态规划。当找到一条返祖边,也就是low[s]dfn[s]的时候,就把在s之前的点全部弹出并记录在一个强连通分量里直到把s弹出来。


    由于图不一定连通而一个孤点也是一个强连通分量所以只要!dfn[i]就要进行tarjan。最后枚举所有边,要是有不在同一个强连通分量的两个点之间有边,起点这个强连通分量的出度就++,终点的强连通分量的入度就++。然后枚举缩点后的所有强连通分量,用ans1和ans2记录入度为0和出度为0的点的数量,就完了。


    注意事项:
    1、如果所有的点都在一个强连通分量里面,就不需要加边,所以if(cnt
    1)ans=0这个语句是必要的。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<stack>
    #include<algorithm>
    #define maxn 1000010
    using namespace std;
    struct Edge{
    	int u;
    	int v;
    	int next;
    }edge[maxn];
    stack<int>stk;
    int first[maxn],last[maxn],low[maxn],dfn[maxn],belong[maxn],rd[maxn],cd[maxn];
    int node,n,x,hhhh,cnt,ans1,ans2,ans;
    void addedge(int u,int v){
    	edge[++node]=(Edge){u,v,0};
    	if(first[u]==0)first[u]=node;
    	else edge[last[u]].next=node;
    	last[u]=node;
    	return;
    }
    void init(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		while(1){
    			scanf("%d",&x);
    			if(x==0)break;
    			addedge(i,x);
    		}
    	}
    	return;
    }
    void tarjan(int s){
    	low[s]=dfn[s]=++hhhh;
    	stk.push(s);
    	for(int q=first[s];q;q=edge[q].next){
    		int p=edge[q].v;
    		if(dfn[p]==0){
    			tarjan(p);
    			low[s]=min(low[s],low[p]);
    		}
    		else if(!belong[p]){
    			low[s]=min(low[s],dfn[p]);
    		}
    	}
    	if(low[s]==dfn[s]){
    		cnt++;
    		belong[s]=cnt;
    		while(stk.top()!=s){
    			belong[stk.top()]=cnt;
    			stk.pop();
    		}
    		stk.pop();
    	}
    }
    void solve(){
    	for(int i=1;i<=n;i++){
    		if(!dfn[i]){
    			tarjan(i);
    		}
    	}
    	for(int i=1;i<=n;i++){
    		for(int q=first[i];q;q=edge[q].next){
    			int p=edge[q].v;
    			if(belong[i]!=belong[p]){
    				rd[belong[p]]++;
    				cd[belong[i]]++;
    			}
    		}
    	}
    	for(int i=1;i<=cnt;i++){
    		if(rd[i]==0)ans1++;
    		if(cd[i]==0)ans2++;
    	}
    }
    int main(){
    	init();
    	solve();
    	printf("%d
    ",ans1);
    	ans=max(ans1,ans2);
    	if(cnt==1)ans=0;
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    人生苦短,我用python-- Day8
    人生苦短,我用python-- Day7
    人生苦短,我用python-- Day6 面向对象
    人生苦短,我用python-- Day5
    人生苦短,我用python-- Day4
    人生苦短,我用python-- Day3
    人生苦短,我用python-- Day2
    人生苦短,我用python-- Day1
    小康陪你学JAVA--------三大循环之Do-while循环
    小康陪你学JAVA--------三大循环之For循环
  • 原文地址:https://www.cnblogs.com/virtual-north-Illya/p/10045075.html
Copyright © 2011-2022 走看看