先 看 完 题 目 , 别 急 着 看 题 解 了 啦 先看完题目,别急着看题解了啦 先看完题目,别急着看题解了啦
信息传递
- 说白了,就是一道求最小环问题的裸题
- 自己的信息又传回来 => 此图一定有环
- 当有人从别人口中得知自 己的生日时,游戏结束 => 最小环问题
- 杰系一国有向图
- 一般有向图用并查集 & dfs可以求解
好,我们先来看一下并查集怎么做
并查集其实就是找自己的大哥,那怎么用它找环呢?其实就是在找刀哥的过程中,找回自己,就形成了环,那最小环有怎么求呢?在枚举时,用一个cnt来记录每个点回到自己所需要的路程,这就是最后的答案了,完美 !
来来来,话不多说,上代码:
#include<bits/stdc++.h>
#define re register
using namespace std;
const int N = 200000 + 1;
int n, t[N], ans = ~0u >> 1;
inline int Find(int k, int &cnt){
cnt++;//记录环长
return t[k] == k ? k : (Find(t[k], cnt));
// if (t[k] == k) return k;
// else return t[k] = Find(t[k], cnt);
}
signed main(){
scanf("%d", &n);
for(re int i = 1; i <= n; i++) t[i] = i;//初始化
for(re int i = 1; i <= n; i++){
int cnt = 0, num;
scanf("%d", &num);
if(Find(num, cnt) == i) ans = min(ans, cnt);
else t[i] = num;
}
printf("%d", ans);
return 0;
}
好,我们再来看看第二种做法dfs
首先枚举,然后在枚举的过程中用一个vis记录该点是否访问过,以防进入一个死递归, 在用l数组记录枚举的该点到每一点的路程,环的大小就可以用总的路程减去起点第一次到该点时的路程,这就是我们要求的环的大小,最后在取个min值,就好了!
#include<bits/stdc++.h>
#define re register
using namespace std;
const int N = 200000 + 1;
int t[N], n, l[N] = {0}, minn = ~0u >> 1;
bool back[N] = {0}, vis[N] = {0};
//back[i]: 是否会回到原点,形成环
//vis[i]: 是否访问过的点
void dfs(int node, int cnt){
if(vis[node]) return;
if(back[node]) minn = min(minn, cnt - l[node]);
else{
back[node] = 1;
l[node] = cnt;
dfs(t[node], cnt + 1);
vis[node] = 1;
}
}
signed main(){
scanf("%d", &n);
for(re int i = 1; i <= n; i++) scanf("%d", &t[i]);
for(re int i = 1; i <= n; i++) dfs(i, 0);
printf("%d", minn);
fclose(stdin), fclose(stdout);
}