• 信息传递


    题面:https://www.acwing.com/problem/content/519/

    有 n 个同学(编号为 1 到 n)正在玩一个信息传递的游戏。

    在游戏里每人都有一个固定的信息传递对象,其中,编号为 i 的同学的信息传递对象是编号为 TiTi 的同学。 

    游戏开始时,每人都只知道自己的生日。

    之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息,但是每人只会把信息告诉一个人,即自己的信息传递对象)。

    当有人从别人口中得知自己的生日时,游戏结束。

    请问该游戏一共可以进行几轮?


    题目意思很清楚,就是求最小环。先看样例

    法1 :拓扑+电风扇(dfs)

    很容易发现最小环是2->4->3->2

    并且1和5不在环内。

    观察到5没有前驱,而且经过拓扑之后1也是如此,图中就只剩下环

    我们就从头到尾扫一遍。没有前驱就一定在环内,从这个点开始dfs遍历环内的点并标记

    然后用环的大小更新答案

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=2000009;
    int indug[maxn],fa[maxn],vis[maxn],n;
    queue<int>q;
    void tuopu()
    {
        for(int i=1;i<=n;i++)
            if(indug[i]==0)
                q.push(i);
        while(!q.empty())
        {
            int ans=q.front();q.pop();
            if(--indug[fa[ans]]==0)
                q.push(fa[ans]);
        }
    }
    int ans=99999999;
    void dfs(int now,int qi,int s)
    {
        if(fa[now]==qi){
            ans=min(ans,s);
            return;
        }
        if(vis[fa[now]])    return;
        vis[fa[now]]=1;
        dfs(fa[now],qi,s+1);
    }
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            int x;
            cin>>x;
            indug[x]++;
            fa[i]=x;
        }
        tuopu();
        for(int i=1;i<=n;i++)
        {
            if(vis[i])    continue;
            if(indug[i]==0)    continue;
            vis[i]=1;
            dfs(i,i,0);
        }
        cout<<ans+1;
    }
    View Code

    法2:拓扑+并查集

    核心思路不变,仍然是一遍拓扑去掉不在环内的点

    然后对于其余的点找祖先,把祖先的计数器加加

    显然,环内的每一个点都会让祖先加1

    最后扫一遍计数器不为0的点,取最小值

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <string.h>
    #include <math.h>
    #include <queue>
    #include <map>
    #include <stack>
    #include <vector>
    using namespace std;
    int n;
    int indug[200009];
    int vis[200009];
    vector<int>vec[200009];
    void tuopu()
    {
        queue<int>d;
        for(int i=1;i<=n;i++)    if(indug[i]==0)        vis[i]=1,d.push(i);
        while(!d.empty())
        {
            int ans=d.front();
            d.pop();
            for(int i=0;i<vec[ans].size();i++)
            {
                if(--indug[vec[ans][i]]==0)
                    vis[vec[ans][i]]=1,d.push(vec[ans][i]);
            }
        }
    }
    int num[200009],pre[200009];
    int cnt[200009];
    int find(int x)
    {
        if(pre[x]!=x)    pre[x]=find(pre[x]);
        return pre[x];
    }
    void join(int q,int w)
    {
        pre[find(q)]=find(w);
        return;
    }
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++)    pre[i]=i;
        for(int i=1;i<=n;i++)
        {
            int l;cin>>l;
            vec[i].push_back(l);
            indug[l]++;
            join(i,l);
        }
        tuopu();
        for(int i=1;i<=n;i++)
        {
            if(vis[i]==0)
                cnt[find(i)]++;
        }
        int ans=999999;
        for(int i=1;i<=n;i++)
            if(cnt[i]!=0)
                ans=min(ans,cnt[i]);
        cout<<ans; 
    }
    View Code

     法3:带权并查集

    原理的话暂时也不太清楚。以后再来看吧。

    #include<cstdio>
    #include<iostream>
    using namespace std;
    int f[200002],d[200002],n,minn,last;   //f保存祖先节点,d保存到其祖先节点的路径长。 
    int fa(int x)
    {
        if (f[x]!=x)                       //查找时沿途更新祖先节点和路径长。 
        {
            int last=f[x];                 //记录父节点(会在递归中被更新)。 
            f[x]=fa(f[x]);                 //更新祖先节点。 
            d[x]+=d[last];                 //更新路径长(原来连在父节点上)。 
        }
        return f[x];
    }
    void check(int a,int b)
    {
        int x=fa(a),y=fa(b);               //查找祖先节点。 
        if (x!=y) {f[x]=y; d[a]=d[b]+1;}   //若不相连,则连接两点,更新父节点和路径长。 
        else minn=min(minn,d[a]+d[b]+1);   //若已连接,则更新最小环长度。 
        return;
    }
    int main()
    {
        int i,t;
        scanf("%d",&n);
        for (i=1;i<=n;i++) f[i]=i;         //祖先节点初始化为自己,路径长为0。 
        minn=0x7777777;
        for (i=1;i<=n;i++)
        {
            scanf("%d",&t);
            check(i,t);                    //检查当前两点是否已有边相连接。 
        }
        printf("%d",minn);
        return 0;
    }
    View Code
  • 相关阅读:
    关于使用css3属性:transform固定菜单位置,在滑动页面时菜单闪现抖动的问题
    使用iscroll.js插件时,遇到在Android端无法点击a超链的解决办法
    window.external的使用
    SpringMVC的拦截器(Interceptor)和过滤器(Filter)的区别与联系
    chain.doFilter(request,response)含义
    巧用Ajax的beforeSend 提高用户体验
    getRequestDispatcher(path).forward(),,执行完,后面的代码居然还会执行!!!记得加return 啊亲
    @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
    ajax修改表单的值后dom没更新的解决办法
    .net core json序列化首字符小写和日期格式处理
  • 原文地址:https://www.cnblogs.com/iss-ue/p/12487886.html
Copyright © 2020-2023  润新知