【代碼超詳解】POJ 2253 Frogger(最小瓶頸路 · 模板題(需要用到:Kruskal 算法、並查集))

一、題目描述

在這裏插入圖片描述

二、算法分析說明與代碼編寫指導

以下內容是根據 https://oi-wiki.org/graph/mst/ 改寫的:
瓶頸生成樹
定義
無向圖 G 的瓶頸生成樹是這樣的一個生成樹,它的最大的邊權值在 G 的所有生成樹中最小。
性質
最小生成樹是瓶頸生成樹的充分不必要條件。 即最小生成樹一定是瓶頸生成樹,而瓶頸生成樹不一定是最小生成樹。
關於最小生成樹一定是瓶頸生成樹這一命題,可以運用反證法證明:
設最小生成樹中的最大邊權爲 w。如果最小生成樹不是瓶頸生成樹的話,則瓶頸生成樹的最大邊權(所有邊權)都小於 w,我們只需刪去原最小生成樹中的最長邊,用瓶頸生成樹中的一條邊(這樣的邊一定存在,因爲生成樹確保圖上的任意兩點都在生成樹上由唯一的簡單路徑連通)連接刪去邊後形成的兩棵樹,得到的新生成樹一定比原最小生成樹的權值和還要小,這樣就產生了矛盾,原命題得證。
最小瓶頸路
定義
無向圖
中 x 到 y 的最小瓶頸路是這樣的一類簡單路徑,滿足這條路徑上的最大的邊權在所有 x 到 y 的簡單路徑中是最小的。
性質
根據最小生成樹的定義,x 到 y 的最小瓶頸路上的最大邊權等於最小生成樹上 x 到 y 路徑上的最大邊權。雖然最小生成樹不唯一,但是每種最小生成樹 x 到 y 路徑的最大邊權相同且爲最小值。也就是說,每種最小生成樹上的 x 到 y 的路徑均爲最小瓶頸路。
但是,並不是所有最小瓶頸路都存在一棵最小生成樹滿足其爲樹上 x 到 y 的簡單路徑。
由於最小瓶頸路不唯一,一般情況下會詢問最小瓶頸路上的最大邊權。
用 Kruskal 算法求解,在求解過程中只要在當要求最小瓶頸路的最大邊權的兩點被添加到生成樹內就終止算法,返回找到的最大邊權即可。
在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述

三、AC 代碼

1、輸出 double 採用 .f 會 WA,當然鍋還是 POJ 的古董編譯器的。
2、如果採用 C++ 提交,hypot 函數無法通過編譯(LNK2019)。
3、類內重載 < 運算符時要在參數後加 const,否則 Compile Error。

#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
#pragma warning(disable:4996)
using namespace std;
template<size_t n> class union_find {
private:
	unsigned root[n], rank[n];
public:
	union_find<n>() { init(); }
	void init() {
		fill(rank, rank + n, 0);
		for (unsigned i = 0; i < n; ++i)root[i] = i;
	}
	unsigned find_root(const unsigned& v) {
		unsigned r = v, t = v, u;
		if (t == root[v])return v;
		while (r != root[r])r = root[r];
		while (t != r)u = root[t], root[t] = r, t = u;
		return r;
	}
	void merge(const unsigned& u, const unsigned& v) {
		unsigned fu = find_root(u), fv = find_root(v);
		if (rank[fu] <= rank[fv]) { root[fu] = fv; if (rank[fu] == rank[fv])++rank[fv]; }
		else { root[fv] = fu; }
	}
};
struct edge {
	unsigned u, v; double w;
	bool operator<(const edge& rhs) const { return this->w < rhs.w; }
};
struct point { int x, y; };
union_find<201> U; unsigned n, c; vector<point> P; const point o = { 0,0 }; point p; vector<edge> E; edge e;
inline double kruskal() {
	sort(E.begin(), E.end()); vector<edge>::iterator I = E.begin();
	for (;;) {
		if (U.find_root(I->u) != U.find_root(I->v)) {
			U.merge(I->u, I->v);
			if (U.find_root(1) == U.find_root(2))return I->w;
		}
		++I;
	}
}
int main() {
	for (;;) {
		++c; scanf("%u", &n); if (n == 0)return 0;
		P.clear(); P.push_back(o); E.clear(); U.init();
		for (unsigned i = 1; i <= n; ++i) {
			scanf("%d%d", &p.x, &p.y); P.push_back(p);
			for (unsigned j = 1; j < i; ++j) {
				e.u = j; e.v = i; e.w = hypot(P[i].x - P[j].x, P[i].y - P[j].y); E.push_back(e);
				swap(e.u, e.v); E.push_back(e);
			}
		}
		printf("Scenario #%u\nFrog Distance = %.3f\n\n", c, kruskal());
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章