zoukankan      html  css  js  c++  java
  • [LUOGU] P2661 信息传递

    题目描述
    
    有n个同学(编号为1n)正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti同学。
    
    游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息,但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自己的生日时,游戏结束。请问该游戏一共可以进行几轮?
    
    输入输出格式
    
    输入格式:
    输入共2行。
    
    第1行包含1个正整数n表示n个人。
    
    第2行包含n个用空格隔开的正整数T1,T2,……,Tn其中第i个整数Ti示编号为i
    
    的同学的信息传递对象是编号为Ti的同学,TinTii
    
    数据保证游戏一定会结束。
    
    输出格式:
    输出共 1 行,包含 1 个整数,表示游戏一共可以进行多少轮。
    
    输入输出样例
    
    输入样例#1: 复制
    5
    2 4 2 3 1
    输出样例#1: 复制
    3
    说明
    
    样例1解释
    
    “我是图”
    
    游戏的流程如图所示。当进行完第 3 轮游戏后, 4 号玩家会听到 2 号玩家告诉他自
    
    己的生日,所以答案为 3。当然,第 3 轮游戏后, 2 号玩家、 3 号玩家都能从自己的消息
    
    来源得知自己的生日,同样符合游戏结束的条件。
    
    对于 30%的数据, n ≤ 200;
    
    对于 60%的数据, n ≤ 2500;
    
    对于 100%的数据, n ≤ 200000。

    感觉是找一个图里的最小环,数据到200000,但是它极其特殊,每个点出度均为1,边权均为1,邻接表可以退化为一个next数组(链表),然后就是O(跑得过)的遍历问题了。
    先删去不在环上的边,可以发现它们都有一个入度为0的头结点,然后依次遍历直到入度不为1,但还是不写递归的好,虽然这确实很递归,深度万一很大,很可能爆栈,就跟DFS联通块不如BFS安全一样。这时候要将该点(找到的环上的一个点)入度减去1!否则可能存在残余的边。同时计数,取最小值即可。

    //Writer:GhostCai && His Yellow Duck
    
    #include<iostream>
    #include<cstdio>
    #define MAXN 200005
    using namespace std;
    
    inline int read_d(){
        int s=0;
        char c;
        while(c=getchar(),c<'0'||c>'9');
        while(c<='9'&&c>='0') {
            s=s*10+c-'0';
            c=getchar();
        }
        return s;
    }
    
    int n,next[MAXN],out[MAXN];
    int ans=0x7fffffff;
    bool vis[MAXN];
    int main(){
    //  freopen("test.in","r",stdin);
        n=read_d();
        int x;
        for(register int i=1;i<=n;i++){
            x=read_d();
            next[i]=x;
            out[x]++;
        }
        for(register int i=1;i<=n;i++){
            if(out[i]==0){
                int j=i;
                do{
                    vis[j]=1;
                    j=next[j];
                }while(out[j]==1&&j);
                out[j]--;//!! !!
            }
        }
        for(register int i=1;i<=n;i++){
            if(vis[i]) continue;
            int nx=i,cnt=0;
            while(!vis[nx]){
                vis[nx]=1;
                nx=next[nx];
                cnt++;
            }
            if(cnt!=1) ans=min(ans,cnt);//!!
        }
        printf("%d
    ",ans);
        return 0;
    }
    

    20ms可以说是非常快了,但是,但是,有更好的思路。

    免去删边的预处理。
    方法是 一条道搜到黑(环),对每个点用dis数组记录走到当前点的cnt,可以发现,找到环的时候,环尾减环首加一就是环的长度。

    //Writer:GhostCai && His Yellow Duck
    
    #include<iostream>
    #include<cstdio>
    #define MAXN 200005
    using namespace std;
    
    inline int read_d(){
        int s=0;
        char c;
        while(c=getchar(),c<'0'||c>'9');
        while(c<='9'&&c>='0') {
            s=s*10+c-'0';
            c=getchar();
        }
        return s;
    }
    
    int n,next[MAXN];//,out[MAXN];
    int ans=0x7fffffff;
    bool vis[MAXN];
    int cnt=0;
    int dis[MAXN];
    
    int main(){
    //  freopen("test.in","r",stdin);
        n=read_d();
        int x;
        for(register int i=1;i<=n;i++){
            x=read_d();
            next[i]=x;
    //      out[x]++;
        }
    //  for(register int i=1;i<=n;i++){
    //      if(out[i]==0){
    //          int j=i;
    //          do{
    //              vis[j]=1;
    //              j=next[j];
    //          }while(out[j]==1&&j);
    //          out[j]--;//!! !!
    //      }
    //  }
        for(register int i=1;i<=n;i++){
            if(vis[i]) continue;
            int nx=i,now=cnt;
            while(!vis[nx]){
                dis[nx]=++cnt;
                vis[nx]=1;
                if(dis[next[nx]]>now) ans=min(ans,dis[nx]-dis[next[nx]]+1);
                nx=next[nx];
            }
    
    //      if(cnt!=1) ans=min(ans,cnt);//!!
        }
        printf("%d
    ",ans);
    }
    

    正常tarjan算法

    #include<iostream>
    #include<stack>
    #include<cstring>
    #define MAXN 200006
    using namespace std;
    
    int n;
    int cnt,ans=1<<30;
    
    int head[MAXN],ecnt;
    struct Edge{
        int to,next;
    }e[MAXN];
    inline void add(int x,int y){
        e[++ecnt].to = y;
        e[ecnt].next = head[x];
        head[x]=ecnt;
    }
    
    bool ins[MAXN];
    stack<int> S;
    int dfn[MAXN],low[MAXN],indexs;
    void tarjan(int id){
    //  cnt=0;
        dfn[id]=low[id]=++indexs;
        S.push(id);
        ins[id]=1;
        for(int i=head[id];i!=-1;i=e[i].next) {
            int v=e[i].to ;
            if(!dfn[v]){
                tarjan(v);
                low[id]=min(low[id],low[v]);
            }else if(ins[v]){
                low[id]=min(low[id],low[v]);
            }
        }
        if(dfn[id]==low[id]){
            int elm=-1;
            cnt=0;
            while(!S.empty()&&elm!=id){
                cnt++;
                elm=S.top();
                ins[elm]=0;
    //          cout<<elm<<" ";
                S.pop() ;
            }
            if(cnt>2) ans=min(ans,cnt);
        }
    }
    
    int main(){
        memset(head,-1,sizeof(head));
        cin>>n;
        int x;
        for(int i=1;i<=n;i++){
            cin>>x;
            add(i,x);
        }
        for(int i=1;i<=n;i++){
    //      cnt=0;
            if(dfn[i]) continue;
            tarjan(i);
    //      cout<<endl;
    
        }
        cout<<ans;
        return 0;
    }

    本文来自博客园,作者:GhostCai,转载请注明原文链接:https://www.cnblogs.com/ghostcai/p/9247526.html

  • 相关阅读:
    SQLite学习第02天:数据类型
    SQLite学习第01天:参考资料
    利用OllyDebug查看程序调用的dll模块
    Qt文件信息获取之QFileInfo
    Qt标准对话框之QColorDialog
    Windows平台下Qt开发环境的搭建
    何时使用引用参数(转)
    OpenCV2学习笔记01:Linux下OpenCV开发环境的搭建
    node实现缓存
    node进阶之用流实现上传文件
  • 原文地址:https://www.cnblogs.com/ghostcai/p/9247526.html
Copyright © 2011-2022 走看看