刷题记录【[USACO08DEC]在农场万圣节Trick or Treat on the Farm】【NOIP2015信息传递
今天是两道很相似的图论水题
1.信息传递
https://www.luogu.org/problemnew/show/P2661
题目描述
有 n个同学(编号为 1 到 n )正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,
其中,编号为 i 的同学的信息传递对象是编号为的同学。游戏开始时,每人都只知道自己的生日。之后每一轮中,
所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象
(注意:可能有人可以从若干人那里获取信息, 但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自 己的生日时,游戏结束。请问该游戏一共可以进行几轮?
输出格式:
1个整数,表示游戏一共可以进行多少轮。
经过分析,本题的目的是求出最小环
本题的图具有一个特点:每一个节点的出度都为1
因此对于任意一个节点,最多在一个环中。
进一步分析,一个点出现在环中的一个必要条件是入度大于等于1
一个点不在环中的充分条件是这个点的入度为0
因此我们采用类似拓扑排序的删边法,将入度为0的点删去并且将其连接的点入度减一
重复这个过程直到没有入度为0 的点
最后所有入度不为0的点都在环中了
最后可以直接dfs(或者其他鬼畜方法)得到每个环的长度就可以了
(代码有的地方写丑了(队列完全可以不用)将就着看一下吧)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstdlib>
using namespace std;
#define N 200006
int t[N],indegree[N],ans = 0x7f7f7f7f,n;
bool vis[N],vist[N];
int dfs(int x)
{
int i = t[x],as=0;
vis[x]=1;
while(i!=x)
{
vis[i]=1;
as++;
i=t[i];
}
return as+1;
}
int main()
{
// freopen("test.in","r",stdin);
scanf("%d",&n);
for(int i = 1; i <= n ; i ++)
{
scanf("%d",t+i);
indegree[t[i]]++;
}
for(int i = 1 ; i <= n; i ++)
if(indegree[i]==0&&!vist[i])
{
vist[i]=1;
queue <int> q;
q.push(i);
while(!q.empty())
{
int x=q.front();
vist[x]=1;
q.pop();
indegree[t[x]]--;
if(indegree[t[x]]==0)
q.push(t[x]);
}
}
for(int i = 1; i <= n; i ++)
if(indegree[i]!=0&&!vis[i])
{
ans = min(ans,dfs(i));
}
cout<<ans;
}
万圣节
https://www.luogu.org/problemnew/show/P2921
题目描述
Every year in Wisconsin the cows celebrate the USA autumn holiday of Halloween by dressing up in costumes and collecting candy that Farmer John leaves in the N (1 <= N <= 100,000) stalls conveniently numbered 1…N.
Because the barn is not so large, FJ makes sure the cows extend their fun by specifying a traversal route the cows must follow. To implement this scheme for traveling back and forth through the barn, FJ has posted a ‘next stall number’ next_i (1 <= next_i <= N) on stall i that tells the cows which stall to visit next; the cows thus might travel the length of the barn many times in order to collect their candy.
FJ mandates that cow i should start collecting candy at stall i. A cow stops her candy collection if she arrives back at any stall she has already visited.
Calculate the number of unique stalls each cow visits before being forced to stop her candy collection.
输入格式:
-
Line 1: A single integer: N
-
Lines 2…N+1: Line i+1 contains a single integer: next_i
输出格式: -
Lines 1…N: Line i contains a single integer that is the total number of unique stalls visited by cow i before she returns to a stall she has previously visited.
这道题与上一道题很相似。
可以借助上一题的思路为基础进行讨论:
若奶牛的起点在环上,则答案就是环长
若不在环上,则为连接到的环的长度加上连接点与起点的距离
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <algorithm>
using namespace std;
#define N 100006
int to[N],n,ind[N],len[N];
bool vis[N],del[N];
int dfs(int x)
{
if(vis[x])return 0;
vis[x]=1;
return dfs(to[x])+1;
}
int dfs1(int x)
{
if(len[x])return len[x];
return len[x]=dfs1(to[x])+1;
}
int main()
{
scanf("%d",&n);
for(int i =1; i <= n; i ++)
{
scanf("%d",to+i);
ind[to[i]]++;
}
for(int i = 1; i <= n ; i ++)
if(ind[i]==0&&!del[i])
{
int x = i;
while(ind[x]==0)
{
del[x]=1;
ind[to[x]]--;
x=to[x];
}
}
for(int i = 1; i <= n ; i ++)
if(ind[i]!=0&&!vis[i])
{
// vis[i]=1;
len[i] = dfs(i);
int j = to[i];
while(j!=i)
{
len[j]=len[i];
j=to[j];
}
}
for(int i = 1; i <= n; i ++)
if(ind[i]==0&&len[i]==0)
{
dfs1(i);
}
for(int i = 1 ; i<= n ; i++)
printf("%d\n",len[i]);
}