信息傳遞(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++