LUOGU P2661 信息傳遞

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章