[USACO5.3]校园网Network of Schools,洛谷之提高历练地,强连通分量

正题

      [USACO5.3]校园网Network of Schools

      第一问:求至少从多少个节点开始,可以遍历整个图。

      第二问:求至少加上多少条边,使得无论从哪个节点开始,都可以遍历整张图

      首先声明先缩环成点。

      第一问就是求入度为0的点即可,因为不是入度为0的话,那么肯定可以从另外一个点传递信息过来。

      第二问好像很烦,要求的是加上多少条边可以使图成环。

      那么很明显啊,如果成环那么每个点都有入度和出度。

      所以我们就想一想答案是不是入度为0或者出度为0的点数的最大值。

      好像是对的。。。

      怎么证明

      首先无视边的方向,它肯定是一棵树。

      如果入度比出度大,那么必定能从出度为0的点连边到入度为0的点。剩下的入度为0的点从其他不是入度为0的点随便连即可。

      反之则同理。

      所以跑一便缩点,用ans1记录入度为0的点数,ans2记录出度为0的点数,输出答案即可。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<stack>
#include<vector>
using namespace std;

int n,x;
struct edge{
    int x,y,next;
}s[10010];
struct node{
    int dfn,low;
}op[110];
int first[110],where[110];
int len=0;
stack<int> f;
int now=0;
bool tf[110];
vector<int> T[110];
int fa[110];
int in[110],out[110];
int tot=0;

void ins(int x,int y){
    len++;
    s[len].x=x;s[len].y=y;s[len].next=first[x];first[x]=len;
}

int findpa(int x){
    if(fa[x]!=x) return fa[x]=findpa(fa[x]);
    return fa[x];
}

void Tarjan(int x){
    op[x].dfn=op[x].low=++now;
    tf[x]=true;f.push(x);
    for(int i=first[x];i!=0;i=s[i].next){
        int y=s[i].y;
        if(op[y].dfn==0){
            Tarjan(y);
            if(op[y].low<op[x].low) op[x].low=op[y].low;
        }
        else if(tf[y]==true)
            if(op[y].dfn<op[x].low) op[x].low=op[y].dfn;
    }
    if(op[x].dfn==op[x].low){
        tot++;
        while(1){
            int k=f.top();
            f.pop();
            tf[k]=false;
            T[tot].push_back(k);
            where[k]=tot;
            if(k==x) break;
        }
    }
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        while(1){
            scanf("%d",&x);
            if(x==0) break;
            ins(i,x);
        }	
    for(int i=1;i<=n;i++)
        if(op[i].dfn==0) Tarjan(i);
    for(int i=1;i<=tot;i++)
        fa[i]=i;
    int ans1=0,ans2=0;
    for(int i=len;i>=1;i--){
    	if(where[s[i].x]==where[s[i].y]) continue;
        in[where[s[i].y]]++;
        out[where[s[i].x]]++;
    }
    for(int i=1;i<=tot;i++){
    	    if(in[i]==0) ans1++;
	    if(out[i]==0) ans2++;
	}
    if(tot==1) printf("1\n0");//如果连通分块的数量为1,那么很明显不用连边
    else printf("%d\n%d",ans1,max(ans2,ans1));
    return 0;
}

       

猜你喜欢

转载自blog.csdn.net/deep_kevin/article/details/80038730
今日推荐