1. 程式人生 > 實用技巧 >P2941 [USACO09FEB]Surround the Islands S 題解

P2941 [USACO09FEB]Surround the Islands S 題解

1.題目大意

這道題我認為配不上藍題,我覺得這不科學,這道題的神奇之處在於它的題面描述十分的神奇。

中文翻譯可能有一些問題,我們看看英文翻譯,它要求我們在去到一個島後立馬回到原來的島。這不就是一個菊花圖嗎??

2.程式碼實現

我們先用並查集來把環縮掉,然後直接列舉以每個節點為中心的菊花圖,記得把答案乘\(2\)(我們要去一趟,回來一趟,共兩趟),時間複雜度\(O(n^2)\)

有些大佬用Tarjan縮點,這樣未免太麻煩,這個圖是一個不聯通的,每兩個環之間沒有其他的邊連線!!!

#include <bits/stdc++.h>
using namespace std;
int n, fa[505];
int dis[505][505];
int ans = INT_MAX, tot;
int opt[505];
int getfa(int x) { //並查集的核心操作 
	if(x == fa[x]) return x;
	return fa[x] = getfa(fa[x]); //路徑壓縮優化 
}
int main() {
	cin >> n;
	memset(dis, 127, sizeof(dis)); //初始化兩點之間的距離 
	for (int i = 1; i <= n ; i ++) 
		fa[i] = i; //並查集初始化 
	for (int i = 1; i <= n ; i ++) {
		int x, y;
		scanf("%d%d", &x, &y);
		int fx = getfa(x), fy = getfa(y);
		if (fx != fy) //如果在同一個並查集裡面,說明這條線段是那個島嶼的一部分 
			fa[fx] = fy;
	}
	for (int i = 1; i <= n ; i ++) //統計總共有多少島嶼,並把它編號 
		if (fa[i] == i) 
			opt[++tot] = i;
	for (int i = 1; i <= n ; i ++) {
		int xxx = getfa(i);
		for (int j = 1; j <= n ; j ++) {
			int yyy = getfa(j);
			int x;
			scanf("%d", &x);
			dis[xxx][yyy] = min(dis[xxx][yyy], x);//在輸入的時候計算兩個島嶼之間的最短路徑
			                                      //注意,我們算的不是最短路,而是兩個島嶼之間的直接路徑的長度 
		}
	}

	for (int i = 1; i <= tot; i ++) { //以每個島為菊花圖的中心 
		int sum = 0;
		for (int j = 1; j <= tot; j ++) {//列舉其他島 
			if (i == j)  
				continue;
			sum += dis[opt[i]][opt[j]];
		}
		ans = min(ans, sum);
	}
	cout << (ans << 1) << endl; //輸出要乘二!!! 
	return 0;
}

3.注意事項

這個程式碼中有幾個地方需要注意,一個是在答案輸出的時候時候要乘二,因為要往返一次,第二個是並查集要記得初始化!!!!(我調這個花了半個小時,我太菜了)。

完結撒花!!