[NOIP2015]Day1 T2 信息传递

来源:互联网 发布:金融数据分析基于r 编辑:程序博客网 时间:2024/06/04 19:45

题意:知道n个人以及他们信息传递的对象,每一轮传递一次自己知道的信息,问最少几轮使得某个人能听到自己的信息。
分析:果断按着样例画了一张有向图,如下:
样例输入
5
2 4 2 3 1

样例输出
3

这已经够明显了,很容易将题目转化为求有向图最小的环。因为只有形成了一个环,一个人的信息才有可能传递回自己,下面讲两种解法。

一、拓扑排序。每次将入度为0的点放入队列,再一个个遍历,我们知道环上的点是不会在拓扑序中的,故可将拓扑排序得到的点排除,因为它们不是环上的点,接下来遍历每个环就行了。

#include<stdio.h>#include<algorithm>#include<iostream>#define oo 2000000000#define M 200005using namespace std;template <class T>inline void Rd(T &res){    char c;res=0;int k=1;    while(c=getchar(),c<48&&c!='-');    if(c=='-'){k=-1;c='0';}    do{        res=(res<<3)+(res<<1)+(c^48);    }while(c=getchar(),c>=48);    res*=k;}int n,ans;int T[M];//记每个人的信息传递对象int degree[M];//记每个点的入读int Q[M],f[M],mark[M];int main(){    int L=0,R=0;    Rd(n);    for(int i=1;i<=n;i++){        Rd(T[i]);        degree[T[i]]++;//增加传递对象入度    }    for(int i=1;i<=n;i++)    if(degree[i]==0)Q[R++]=i;//初始入度为0的点入队    while(L<R){        int u=Q[L++];        f[u]=1;        int v=T[u];        degree[v]--;        if(degree[v]==0)Q[R++]=v;//入度为0的点入队    }    ans=oo;    for(int i=1;i<=n;i++){//剩下的是环上的点,每次遍历一个环并将环上的点标记        if(f[i]||mark[i])continue;        mark[i]=1;        int k=i,res=1;        while(T[k]!=i){            k=T[k];            mark[k]=1;            res++;        }        if(res>1)ans=min(ans,res);    }    printf("%d\n",ans);    return 0;}

二、强连通分量。我们知道每一个环都是一个强连通分量,故可直接把强连通分量敲上去。

#include<stdio.h>#include<string.h>#include<vector>#include<algorithm>#include<iostream>#define oo 2000000000#define M 200005using namespace std;template <class T>inline void Rd(T &res){    char c;res=0;int k=1;    while(c=getchar(),c<48&&c!='-');    if(c=='-'){k=-1;c='0';}    do{        res=(res<<3)+(res<<1)+(c^48);    }while(c=getchar(),c>=48);    res*=k;}vector<int>G[M];vector<int>rG[M];vector<int>vs;int used[M],belong[M];void add_edge(int u,int v){//每次建边的同时建一条反向边    G[u].push_back(v);    rG[v].push_back(u);}void dfs(int u){//正向dfs    used[u]=1;    for(int i=0;i<G[u].size();i++){        int v=G[u][i];        if(!used[v])dfs(v);    }    vs.push_back(u);}void rdfs(int u,int cnt){//反向dfs    used[u]=1;    belong[u]=cnt;    for(int i=0;i<rG[u].size();i++){        int v=rG[u][i];        if(!used[v])rdfs(v,cnt);    }}int scc(int n){//求强连通分量    memset(used,0,sizeof(used));    for(int i=1;i<=n;i++)    if(!used[i])dfs(i);    memset(used,0,sizeof(used));    int cnt=0;    for(int i=vs.size()-1;i>=0;i--)    if(!used[vs[i]])rdfs(vs[i],++cnt);    return cnt;}int n,cnt[M],ans;int main(){    int x;    Rd(n);    for(int i=1;i<=n;i++){        Rd(x);        add_edge(i,x);    }    int V=scc(n);    ans=oo;    for(int i=1;i<=n;i++){        cnt[belong[i]]++;    }    for(int i=1;i<=V;i++)    if(cnt[i]>1)ans=min(ans,cnt[i]);    printf("%d\n",ans);    return 0;}

刚看到这题的时候就想到要求环了,于是想敲强连通分量,但是不会敲,就敲臭了……
5555555~~~~~~~~~

0 0
原创粉丝点击