【拓扑排序/强联通分量】【NOIP201402】信息传递

信息传递

题目描述

  有n个同学(编号为1到n)正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti同学。

  游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息,但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自己的生日时,游戏结束。请问该游戏一共可以进行几轮?

输入输出格式

  输入格式:

  输入共2行。

  第1行包含1个正整数n表示n个人。

  第2行包含n个用空格隔开的正整数T1,T2,……,Tn其中第i个整数Ti示编号为i的同学的信息传递对象是编号为Ti的同学,Ti≤n且Ti≠i

  数据保证游戏一定会结束。

  输出格式:

  输出共 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。

俺的题解

  很明显,这道题可以转换为图论中求最小环的问题。

  

  (本来还找了一下画图论的图的软件,后来发现PPT的功能已经足够强大= =)

  把每个同学当做节点,i和Ti之间连有向边。

  去年刷到这道题时一直觉得环的特征不明显,今年对图论理解深刻了点,就好些了……

  求环就有很多种方式咯,在这里说两种。

方法一:拓扑排序

    拓扑排序时会不断删掉入度为0的点,然而环中的点入度永远不会为0,于是环被保留了下来。

    凭借这个性质搜索剩下的环中最小的长度。 

   方法二:求强连通分量

    因为每同学都只有一位固定对象,所以每个节点出度都为1。此时求强连通分量等同于求环。

    傻X错误:这张图可能不连通!比如:

  

  所以需要循环tarjan...o(╯□╰)o

俺的代码

  方法一:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<queue>
 4 #include<algorithm>
 5 using namespace std;
 6 int n;
 7 int ans=10000000;
 8 struct node
 9 {
10     int next;
11     int cnt;
12     bool bj;
13     node(){next=0;cnt=0;bj=0;}
14 }a[200001];
15 queue <node> que;
16 int main()
17 {
18     cin>>n;
19     for(int i=1;i<=n;i++)
20     {
21         cin>>a[i].next;
22         a[a[i].next].cnt++;//记录入度
23     }
24     //拓扑
25     for(int i=1;i<=n;i++)
26     {
27         if(a[i].cnt == 0)
28         {
29             que.push(a[i]);
30             a[i].bj=1;
31         }
32     }
33     while(!que.empty())
34     {
35         node i=que.front();
36         que.pop();
37         if(--a[i.next].cnt==0)
38         {
39             que.push(a[i.next]);
40             a[i.next].bj=1;
41         }
42     }
43
44     for(int i=1;i<=n;i++)
45     {
46         if(a[i].bj==0)
47         {
48             int j=i,count=0;
49             do
50             {
51                 count++;
52                 a[j].bj=1;
53                 j=a[j].next;
54             }while(j!=i);
55             ans=min(ans,count);
56         }
57     }
58     cout<<ans;
59     return 0;
60 }

点击就送屠龙宝刀

  方法二:

 1 /*
 2 */
 3 #include<iostream>
 4 #include<cstdio>
 5 #include<cstring>
 6 #include<vector>
 7 using namespace std;
 8
 9 int n,scc=0,idx=0,s[200700],low[200700],dfn[200700],top=0;
10 bool in_stack[200700];
11 int ans=10000000;
12 vector<int> edge[200700];
13 int length[200700];
14
15 void tarjan(int u) {
16     dfn[u] = low[u] = ++idx;//init
17     s[top++] = u;//压栈
18     in_stack[u] = true;
19     for (int i = 0; i < edge[u].size();i++) {
20         int v = edge[u][i];
21         if (!dfn[v]) {//未被访问过
22             tarjan(v);//continue
23             low[u] = min(low[u], low[v]);
24         }
25         else if (in_stack[v]) {//如果还在栈内
26             low[u] = min(low[u], dfn[v]);
27         }
28     }
29     int size=0;
30     if (dfn[u] == low[u]) {//如果u是强联通的根
31         do {
32             size++;
33             in_stack[s[--top]] = false;//退栈
34         } while (s[top] != u);//走完一个环返回
35         if(size != 1)
36             ans=min(ans,size);
37     }
38 }
39 int main(){
40     scanf("%d",&n);
41     for(int i=1;i<=n;i++){
42         int v;
43         scanf("%d",&v);
44         edge[i].push_back(v);
45     }
46     for(int i=1;i<=n;i++)
47         tarjan(i);
48     printf("%d",ans);
49     return 0;
50 }

点击就送屠龙宝刀

时间: 2024-08-26 00:36:14

【拓扑排序/强联通分量】【NOIP201402】信息传递的相关文章

POJ 2186-Popular Cows (图论-强联通分量Korasaju算法)

题目链接:http://poj.org/problem?id=2186 题目大意:有n头牛和m对关系, 每一对关系有两个数(a, b)代表a牛认为b牛是“受欢迎”的,且这种关系具有传递性, 如果a牛认为b牛“受欢迎”, b牛认为c牛“受欢迎”, 那么a牛也认为c牛“受欢迎”. 现在想知道有多少头牛受除他本身外其他所有牛的欢迎? 解题思路:如果有两头或者多头牛受除他本身外其他所有牛的欢迎, 那么在这两头或者多头牛之中, 任意一头牛也受两头或者多头牛中别的牛的欢迎, 即这两头或者多头牛同属于一个强联

强联通分量之kosaraju算法

首先定义:强联通分量是有向图G=(V, E)的最大结点集合,满足该集合中的任意一对结点v和u,路径vu和uv同时存在. kosaraju算法用来寻找强联通分量.对于图G,它首先随便找个结点dfs,求出每个节点最后一次访问的时间戳f(x),然后我们建立反图Gt,接着根据倒序的时间戳来dfs每个节点,每次dfs到的结点集合就是一个强联通分量.事实上这个算法的思想和拓扑排序类似. 我们来证明它(注意这里面的图指原图,而不是反图): 引理:对于G中的两个强联通分量C和C',若点u属于C,点v属于C',且

POJ 2186 Popular cows(Kosaraju+强联通分量模板)

题目链接:http://poj.org/problem?id=2186 题目大意:给定N头牛和M个有序对(A,B),(A,B)表示A牛认为B牛是红人,该关系具有传递性,如果牛A认为牛B是红人,牛B认为牛C是红人,那么牛A也认为牛C是红人.求被其他所有牛认为是红牛的牛的总数. 解题思路:把所有牛看成顶点,把有序对(A,B)看成从A到B的有向边,那么题目就变成了求所有顶点都可到达的顶点的总数.我们可以得到一个结论,如果一个强连通分量里有一头牛被认为是红人,那么该强联通分量里的所有牛都是红人,这显然是

[BZOJ1051] [HAOI2006] 受欢迎的牛 (强联通分量)

Description 每一头牛的愿望就是变成一头最受欢迎的牛.现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎. 这 种关系是具有传递性的,如果A认为B受欢迎,B认为C受欢迎,那么牛A也认为牛C受欢迎.你的任务是求出有多少头 牛被所有的牛认为是受欢迎的. Input 第一行两个数N,M. 接下来M行,每行两个数A,B,意思是A认为B是受欢迎的(给出的信息有可能重复,即有可 能出现多个A,B) Output 一个数,即有多少头牛被所有的牛认为是受欢迎的. Sample Input 3

codevs 2822 爱在心中 tarjan(强联通分量)

2822 爱在心中 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description “每个人都拥有一个梦,即使彼此不相同,能够与你分享,无论失败成功都会感动.爱因为在心中,平凡而不平庸,世界就像迷宫,却又让我们此刻相逢Our Home.” 在爱的国度里有N个人,在他们的心中都有着一个爱的名单,上面记载着他所爱的人(不会出现自爱的情况).爱是具有传递性的,即如果A爱B,B爱C,则A也爱C.如果有这样一部分人,他们彼此都相爱,则他们就超越了一

Light OJ 1034 - Hit the Light Switches(强联通分量)

题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1034 题目大意:有n盏灯,有m个关系, 关系a,b表示如果a灯开关打开那么b灯也会亮起来, 现在求至少需要打开多少开关使所有灯都亮. 题目思路:先由强联通分量缩点, 得到DAG图, 然后根据DAG图,求出有多少入度为0的点, 即为所求. 代码如下: #include<bits/stdc++.h> using namespace std; const int N = 1000

【小结】强联通分量分解

强联通分量 在一个有向图的顶点子集S中,对?(u,v),如果都能找到一条从u到v的路径,那么就称S是强联通的.如果向S中加入任何一个其他顶点后S都不再是强联通的,就称S时原图的一个强联通分量. 显然,如果把所有的强联通分量都缩点,原图将变成一个DAG SCC的求解可通过两次dfs实现,第一次在原图中后续遍历,标号:第二遍将所有边反向后,从编号最大的点开始遍历,每次都可得到一个SCC. #include <cstdio> #include <cstring> #include <

[CF #236 (Div. 2) E] Strictly Positive Matrix(强联通分量)

题目:http://codeforces.com/contest/402/problem/E 题意:给你一个矩阵a,判断是否存在k,使得a^k这个矩阵全部元素都大于0 分析:把矩阵当作01矩阵,超过1的都当作1,那么a矩阵可表示一个有向图的走一次的连通性,则a^k表示有向图走K次的连通性.既然要求最后都没0,即走了K次后,每个点都能互通,这也说明这个图必然是只有一个强联通分量.于是判断k的存在有无,也就是判断a矩阵表示的有向图是不是只有一个强联通分量.

51nod 1076 2条不相交的路径 无向图强联通分量 trajan算法

1076 2条不相交的路径 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题  收藏  关注 给出一个无向图G的顶点V和边E.进行Q次查询,查询从G的某个顶点V[s]到另一个顶点V[t],是否存在2条不相交的路径.(两条路径不经过相同的边) (注,无向图中不存在重边,也就是说确定起点和终点,他们之间最多只有1条路) Input 第1行:2个数M N,中间用空格分开,M是顶点的数量,N是边的数量.(2 <= M <= 25000, 1 <= N <=