信息传递(from luogu)
题面 懒得复制了
题目分析
根据题意,我们可以将题目中信息传递的路径以图的方式表现出来
同时,信息的传递时单向的
如下图(样例)
可以很明显的看出,如果符合题意的结束条件便是本人知道自己的信息,也就是走完一个环的流程
但是这个环的长度是有优先之分的,如图:
可以得到,真正流程的结束是内部黄色线条形成的环
所以我们可以得到,流程的数值便是最小环的边数
此时,我的第一想法便是——DFS
尝试每一个点走到底,判断是否可以形成环,同时对经过的点打标记
并且记录环的可能边数来打擂台
这样会超时3个点,70分
之后,我们来思考一下
对于每一个点最终传向的值,便是其祖先,那么如果是环的话,祖先便是自身,由此可得——并查集
对于当前点通向的下一个点,我们寻其祖先,如果形成环——祖先便是现在的点
反之,根据题意下一个点便是我们的当前点的祖先(可以看上图辅助理解)
同时环的边数也便是递归的深度
这样我们便得到了满分 (我太蒻了,为刚开始什么想不到)
但是有一点需要注意——不能路径压缩
如果路径压缩,我们在求深度时会一步到位 GG
总而言之,这题便是并查集求最小环
代码
70分DFS
#include <bits/stdc++.h>
using namespace std;
int v[200009];
int n,s,ans = INT_MAX;
bool jud[200009];
bool j;
void search(int to,int sum) { //to是下一个,sum是当前可能的环的边数
if (to == s) { ans = min(ans,sum); return ; } //形成环,打擂台
if (jud[to]) return ; //重复走了——前面的行不通,回头
jud[to] = true; //打标记
search(v[to],sum+1); //递进
}
int main( ) {
scanf("%d",&n);
for (int i = 1; i <= n; i++) scanf("%d",&v[i]);
for (int i = 1; i <= n; i++) {
memset(jud,false,sizeof(jud)); //每一次初始化
s = i; //确定终点
search(v[i],1); //开搜
}
printf("%d",ans);
return 0;
}
100分并查集
#include <bits/stdc++.h>
using namespace std;
int n,ans = INT_MAX,dep;
int f[200009],v[200009];
int find(int x) {
dep++;
if (x == f[x]) return x;
// f[x] = find(f[x]); 不要路径压缩!!!
// return f[x];
find(f[x]);
}
int main() {
scanf("%d",&n);
for (int i = 1; i <= n; i++) scanf("%d",&v[i]);
for (int i = 1; i <= n; i++) f[i] = i; //初始化
for (int i = 1; i <= n; i++) {
dep = 0; //环的边数
if (find(v[i]) == i) ans = min(ans,dep); //如果这是一个环,可以试试打擂台
else
f[i] = v[i]; //不是,下一个就是祖先
}
printf("%d",ans);
return 0;
}
crx CSP-J/S RP++