一、信息传递
有 n 个同学(编号为 1 到 n)正在玩一个信息传递的游戏。
在游戏里每人都有一个固定的信息传递对象,其中,编号为 i 的同学的信息传递对象是编号为 Ti 的同学。
当有人从别人口中得知自己的生日时,游戏结束。
请问该游戏一共可以进行几轮?
输入样例:
5
2 4 2 3 1
输出样例:
3
方法一:暴力求环
- 第i个人会把生日告诉a[i],如果你要求环的话,则i的父亲应该定义为a[i],即i指向a[i];为什么?因为如果有环的话,则下一次找的时候一定会有 a[i] 指向 i(i->a[i]->......->i),即当
find(a[i])=i
成立时
注:此解法不能用路劲压缩
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+5;
int n, sz, fa[N], ans=N;
int find(int u) {
sz++;
return fa[u]==u ? u : find(fa[u]);
}
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n; for (int i=1; i<=n; i++) fa[i]=i;
for (int i=1; i<=n; i++) {
int x; cin>>x, sz=0;
int fx=find(x);
if (fx==i) {
ans=min(ans, sz);
} else {
fa[i]=x;
}
}
cout<<ans;
return 0;
}
复杂度分析
- Time:\(O(n^2)\),应该是数据比较水
- Space:\(O(n)\),
方法二:记忆化优化
复杂度分析
- Time:\(O()\),
- Space:\(O()\),
二、银河英雄传说
有T条指令,每条指令格式为以下两种之一:
M i j,表示让第i号战舰所在列的全部战舰保持原有顺序,接在第j号战舰所在列的尾部。
C i j,表示询问第i号战舰与第j号战舰当前是否处于同一列中,如果在同一列中,它们之间间隔了多少艘战舰
输入 T、op,i,j
数据范围
N≤30000,T≤500000
输入样例:
4
M 2 3
C 1 2
M 2 4
C 4 2
输出样例:
-1
1
方法一:并查集
假设 d[i]表示结点 i 和根节点 root 之间的战舰数量,那么 x、y 之间的战舰数量则为 |d[x]-d[y]|
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int fa[N], sz[N], d[N];
int find(int u) {
if (fa[u]==u) return u;
int root=find(fa[u]); //整棵树的根
d[u]+=d[fa[u]]; //因为路径压缩之后u已经指向根了,所以在更新之前趁早维护d[u]
return fa[u]=root;
}
void merge(int u, int v) {
int fu=find(u), fv=find(v);
if (fu!=fv) {
fa[fu]=fv;
d[fu]=sz[fv];
sz[fv]+=sz[fu];
}
}
int query(int u, int v) {
int fu=find(u), fv=find(v);
if (fu!=fv) return -1;
return abs(d[u]-d[v])-1;
}
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t; cin>>t; for (int i=1; i<N; i++) fa[i]=i, sz[i]=1;
while (t--) {
char op;
int a,b; cin>>op>>a>>b;
if (op=='M') {
merge(a,b);
} else {
cout<<query(a,b)<<'\n';
}
}
return 0;
}