[kuangbin帶你飛]專題六 做題順序與題解 【最小生成樹】

隨便說點:

博主正在刷kuangbin專題的題目,初學者,沒接觸過什麼算法,刷題的初衷是備戰藍橋杯,後來發現了算法資料大多是針對acm的,挑選kuangbin專題入門也是如此,畢竟這樣分類看起來比較有目的的刷題。
所以有的代碼比較冗餘,越往後面的題解代碼變化越大,
代碼的風格和算法思想也是在一步步的學習和進步,多多包容,互相借鑑。

建議做題順序:
做最小生成樹專題沒最短路和並查集專題時那麼吃力,題目難度有下降。所以把題解都整合在一篇博客裏

最小生成樹基礎:1 2 4 5 6 9 12 14
最小生成樹應用:10 3 8 7
這是我做完一遍之後覺得比較好的做題順序,由易到難,相同類型題放在一起加深理解。

1.POJ 1251 Jungle Roads

把字符轉化成整型,然後套用模板

//poj1251
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 28, INF = 0x3f3f3f3f;
int maps[N][N],visit[N];
int d[N];
int n;
int prim()
{
	int ans = 0;
	for (int i = 1; i < n; i++)
		d[i] = maps[0][i];
	d[0] = 0;
	for (int i = 1; i < n; i++)
	{
		int t = -1;
		for (int j = 1; j < n; j++)
		{
			if (!visit[j])
				if (d[j] < d[t] || t == -1)
					t = j;
		}
		visit[t] = 1;
		ans += d[t];
		for (int j = 1; j < n; j++)
			if (!visit[j])
				d[j] = min(d[j], maps[t][j]);
	}
	return ans;
}
int main()
{
	while (~scanf("%d", &n),n)
	{
		memset(maps, 0x3f, sizeof(maps));
		memset(visit, 0, sizeof(visit));
		for (int i = 0; i < n-1; i++)
		{
			char u;
			int t;
			scanf(" %c %d", &u, &t);
			while (t--)
			{
				char v;
				int w;
				scanf(" %c %d", &v,&w);
				maps[i][v - 'A'] = w;
				maps[v - 'A'][i] = w;
			}
		}
		printf("%d\n", prim());
	}
	return 0;
}

2.POJ 1287 Networking

模板題

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 55, INF = 0x3f3f3f3f;
int maps[N][N],visit[N];
int d[N];
int n, m;
int prim()
{
	int ans = 0;
	for (int i = 2; i <= n; i++)
		d[i] = maps[1][i];
	d[1] = 0;
	for (int i = 2; i <= n; i++)
	{
		int t = -1;
		for (int j = 2; j <= n; j++)
		{
			if (!visit[j])
				if (d[j] < d[t] || t == -1)
					t = j;
		}
		visit[t] = 1;
		ans += d[t];
		for (int j = 2; j <= n; j++)
			if (!visit[j])
				d[j] = min(d[j], maps[t][j]);
	}
	return ans;
}
int main()
{
	while (~scanf("%d%d", &n, &m), n)
	{
		memset(maps, 0x3f, sizeof(maps));
		memset(visit, 0, sizeof(visit));
		for (int i = 0; i < m; i++)
		{
			int u, v, w;
			scanf("%d%d%d", &u, &v, &w);
			if (w < maps[u][v]) {
				maps[u][v] = w;
				maps[v][u] = w;
			}
		}
		printf("%d\n", prim());
	}
	return 0;
}

3.POJ 2031 Building a Space Station

prim,注意 若兩圓心距離-半徑之和< =0則兩點權值設置爲0

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 105;
const double INF = 1e15;
int visit[N];
double d[N];
int n,m;
struct node {
	double  x, y, z, r;
}maps[N];
double getDis(double x1, double y1,double z1, double x2, double y2, double z2 )
{
	return sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2)+ (z1 - z2)*(z1 - z2));
}
double prim()
{
	memset(visit, 0, sizeof(visit));
	for (int i = 1; i <= n; i++)
		d[i] = INF;
	double ans = 0;
	for (int i = 2; i <= n; i++) {
		double len = getDis(maps[1].x, maps[1].y, maps[1].z, maps[i].x, maps[i].y, maps[i].z);
		double r = maps[1].r+ maps[i].r;
		if (r >= len)	d[i] = 0;
		else d[i] = len-r;
			
	}	
	d[1] = 0;
	for (int i = 2; i <= n; i++)
	{
		int t = -1;
		for (int j = 2; j <= n; j++)
		{
			if (!visit[j])
				if (d[t] > d[j]|| t == -1)
					t = j;
		}
		visit[t] = 1;
		ans += d[t];
		for (int j = 2; j <= n; j++)
			if (!visit[j]) {
				double len = getDis(maps[t].x, maps[t].y, maps[t].z, maps[j].x, maps[j].y, maps[j].z);
				double r = maps[t].r + maps[j].r;
				if (r >= len)len = 0;
				else len = len - r;
				d[j] = min(d[j], len);
			}
				
	}
	return ans;
}
int main()
{
	while (scanf("%d", &n),n)
	{
		for (int i = 1; i <= n; i++)
			scanf("%lf%lf%lf%lf", &maps[i].x, &maps[i].y, &maps[i].z, &maps[i].r);
		printf("%.3lf\n", prim());
	}
	return 0;
}



4.POJ 2421 Constructing Roads

模板題

//poj2421
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 105;
const int M = N * N;
const double INF = 1e15;
int fa[N];
int n, m, cnt = 0;;

struct Node {
	int u, v, w;
	bool operator < (const Node& a) const {
		return w < a.w;
	}
}e[M];

int find(int x)
{
	return fa[x] == -1 ? x : fa[x] = find(fa[x]);
}
void unite(int x, int y)
{
	int fx = find(x), fy = find(y);
	if (fx == fy)return;
	fa[fy] = fx;
}

int kustral()
{
	sort(e, e + n * n);
	int ans = 0;
	for (int i = 0; i <cnt; ++i) {
		if (e[i].u == e[i].v)continue;
		if (find(e[i].u) != find(e[i].v)) {
			unite(e[i].u, e[i].v);
			ans += e[i].w;
		}
	}
	return ans;
}
int main()
{
	while (~scanf("%d", &n))
	{
		memset(fa, -1, sizeof(fa));
		cnt = 0;
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
			{
				scanf("%d", &e[cnt].w);
				e[cnt].u = i, e[cnt].v = j;
				cnt++;
			}
		scanf("%d", &m);
		for (int j = 0; j < m; j++)
		{
			int u, v;
			scanf("%d%d", &u, &v);
			unite(u, v);
		}
		printf("%d\n", kustral());
	}
	return 0;
}

5.ZOJ 1586 QS Network

模板題,邊的權值=各自的適配器+道路的花費

//zoj 1586
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 1e3+10;
int visit[N];
int maps[N][N];
int qsNode[N];
int d[N];
int n;
int prim()
{
	int ans = 0;
	memset(visit, 0, sizeof(visit));
	for (int i = 2; i <= n; i++)
		d[i] = maps[1][i];
	d[1] = 0;
	for (int i = 2; i <= n; i++)
	{
		int t = -1;
		for (int j = 2; j <= n; j++)
		{
			if (!visit[j])
				if (d[t] > d[j] || t == -1)
					t = j;
		}
		if (t == -1)break;
		visit[t] = 1;
		ans += d[t];
		for (int j = 2; j <= n; j++)
			if (!visit[j]) {
				d[j] = min(maps[t][j], d[j]);
			}
	}
	return ans;
}
int main()
{
	int t;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d", &n);
		for (int i = 1; i <= n; i++)
			scanf("%d", &qsNode[i]);
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
			{
				scanf("%d", &maps[i][j]);
				maps[i][j] = maps[i][j] + qsNode[i] + qsNode[j];
			}
		printf("%d\n", prim());
	}
	return 0;
}

6.POJ 1789 Truck History

處理一下圖,邊的權值爲兩個字符串對應位置字母不同的個數
然後套用模板即可。

//poj1789
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 2e3+10;
int visit[N];
char str[N][8];
int maps[N][N];
int d[N];
int n;

int prim()
{
	int ans = 0;
	memset(visit, 0, sizeof(visit));
	for (int i = 2; i <= n; i++)
		d[i] = maps[1][i];
	d[1] = 0;
	for (int i = 2; i <= n; i++)
	{
		int t = -1;
		for (int j = 2; j <= n; j++)
		{
			if (!visit[j])
				if (d[t] > d[j] || t == -1)
					t = j;
		}
		if (t == -1)break;
		visit[t] = 1;
		ans += d[t];
		for (int j = 2; j <= n; j++)
			if (!visit[j]) {
				d[j] = min(maps[t][j], d[j]);
			}
	}
	return ans;
}
int main()
{
	while (scanf("%d", &n),n)
	{
		for (int i = 1; i <= n; i++)
			scanf("%s", str[i]);
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
			{
				if (i == j)maps[i][j] = 0;
				else {
					int cnt = 0;
					for (int k = 0; k < 7; k++)
						if (str[i][k] != str[j][k])cnt++;
					maps[i][j] = maps[j][i] = cnt;
				}
			}
		printf("The highest possible quality is 1/%d.\n", prim());
	}
	return 0;
}

7.POJ 2349 Arctic Network

因爲n個衛星可以同時使得n-1條邊免費,故還是kustral
把邊排序,最小生成樹,記錄每次加入的邊值,最後輸出倒數第m條邊即可,m = 免費的邊數+1。
即不算後面n-1條免費的邊

//poj2349
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 505;
const int M = N * N;
const double INF = 1e15;
int fa[N];
int n, m, cnt = 0;

struct Node {
	int u, v;
	double w;
	bool operator < (const Node& a) const {
		return w < a.w;
	}
}e[M];
typedef pair<int, int> pp;
pp point[N];
int find(int x)
{
	return fa[x] == -1 ? x : fa[x] = find(fa[x]);
}
void unite(int x, int y)
{
	int fx = find(x), fy = find(y);
	if (fx == fy)return;
	fa[fy] = fx;
}
double getDis(double x1, double y1, double x2, double y2)
{
	return sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2));
}
double kustral()
{
	sort(e, e + n * (n-1));
	double ans[N];
	int index = 0;
	for (int i = 0; i < cnt; ++i) {
		if (e[i].u == e[i].v)continue;
		if (find(e[i].u) != find(e[i].v)) {
			unite(e[i].u, e[i].v);
			ans[index] = e[i].w;
			index++;
		}
	}
	index = index - m;
	return ans[index];
}
int main()
{
	int t;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d%d", &m, &n);
		memset(fa, -1, sizeof(fa));
		for (int i = 1; i <= n; i++)
			scanf("%d%d", &point[i].first, &point[i].second);
		cnt = 0;
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
			{
				if (i == j)continue;
				e[cnt].u = i, e[cnt].v = j;
				e[cnt].w = getDis(point[i].first, point[i].second, point[j].first, point[j].second);
				cnt++;
			}
		printf("%.2lf\n", kustral());
	}
	return 0;
}

8.POJ 1751 Highways

prim,注意已經存在的邊權值設置爲0,還有就是輸出邊時涉及邊的兩點,可以把d[]數組定義成結構體數組,更新邊的權值時順便記錄下是被哪個點更新的。

//poj1751
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 760, M = 1010;
const double INF = 1e15;
int visit[N];
bool isFree[N][N];
int n, m;
struct node {
	int x, y;
}maps[N];
struct node2 {
	int u;
	double dis;
}d[M];
double getDis(double x1, double y1, double x2, double y2)
{
	return sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2));
}
void prim()
{
	memset(visit, 0, sizeof(visit));
	for (int i = 1; i <= n; i++)
		d[i].dis = INF;
	for (int i = 2; i <= n; i++) {
		if (isFree[1][i]) {
			d[i].dis = 0;
		}
		else {
			d[i].dis = getDis(maps[1].x, maps[1].y, maps[i].x, maps[i].y);
			d[i].u = 1;
		}
	}
	d[1].dis = 0;
	for (int i = 2; i <= n; i++)
	{
		int t = -1;
		for (int j = 2; j <= n; j++)
		{
			if (!visit[j] && d[j].dis != INF)
				if (d[t].dis > d[j].dis || t == -1)
					t = j;
		}
		visit[t] = 1;
		if (t == -1)break;
		if (d[t].dis != 0 && d[t].dis != INF) {
			printf("%d %d\n", d[t].u, t);
		}
		for (int j = 2; j <= n; j++)
			if (!visit[j]) {
				double len;
				if (isFree[t][j])len = 0;
				else {
					len = getDis(maps[t].x, maps[t].y, maps[j].x, maps[j].y);
					
				}
				if (d[j].dis > len) {
					d[j].dis = len;
					d[j].u = t;
				}
			}
	}
}

int main()
{
	while (~scanf("%d", &n))
	{
		memset(isFree, false, sizeof(false));
		for (int i = 1; i <= n; i++)
			scanf("%d%d", &maps[i].x, &maps[i].y);
		scanf("%d", &m);
		for (int i = 1; i <= m; i++)
		{
			int u, v;
			scanf("%d%d", &u, &v);
			isFree[u][v] = isFree[v][u] = true;
		}
		prim();
	}
	return 0;
}

9.POJ 1258 Agri-Net

模板題

//poj1258
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 105, INF = 0x3f3f3f3f;
int maps[N][N],visit[N];
int d[N];
int n;
int prim()
{
	int ans = 0;
	for (int i = 2; i <= n; i++)
		d[i] = maps[1][i];
	d[1] = 0;
	for (int i = 2; i <= n; i++)
	{
		int t = -1;
		for (int j = 2; j <= n; j++)
		{
			if (!visit[j])
				if (d[j] < d[t] || t == -1)
					t = j;
		}
		visit[t] = 1;
		ans += d[t];
		for (int j = 2; j <= n; j++)
			if (!visit[j])
				d[j] = min(d[j], maps[t][j]);
	}
	return ans;
}
int main()
{
	while (~scanf("%d", &n))
	{
		memset(maps, 0x3f, sizeof(maps));
		memset(visit, 0, sizeof(visit));
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
			{
				int w;
				scanf("%d", &w);
				if (w == 0)continue;
				maps[i][j] = w;
				maps[j][i] = w;
			}
		printf("%d\n", prim());
	}
	return 0;
}

10.POJ 3026 Borg Maze

BFS + Prime
暴力對每個A或者S的點進行bfs求到其它點的距離建圖,然後套用最小生成樹模板

//poj 3026
#include<stdio.h>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;

const int N = 505;	//數組開大一點,pojdiscuss說太小會WA
//bfs用的
int visit[N][N];	
int cost[N][N];
char str[N][N];	//str構造bfs的圖maps
int maps[N][N];	
int step[4][2] = { {1,0},{-1,0},{0,-1},{0,1} };
typedef pair<int, int> pp;
vector<pp> point;
//prime用的
int visit_for_prime[N];
int map_for_prime[N][N];
int d[N];
int n, m;
void bfs()
{
	int cnt = 0;
	//求容器中每兩點之間的距離,建prime的圖,編號1-vector的size
	for (vector<pp>::iterator it = point.begin(); it != point.end(); it++)
	{
		cnt++;
		//下方是從一個點開始的bfs
		memset(visit, 0, sizeof(visit));
		memset(cost, 0, sizeof(cost));
		queue<pp> Q;
		Q.push(pp(it->first, it->second));
		visit[it->first][it->second] = 1;
		while (Q.size())
		{
			pp t = Q.front();
			Q.pop();
			visit[t.first][t.second] = 1;
			for (int i = 0; i < 4; i++)
			{
				int x = t.first + step[i][0];
				int y = t.second + step[i][1];
				if (x > 0 && x <= n && y > 0 && y <= m && !visit[x][y] && maps[x][y] != 1) {
					cost[x][y] = cost[t.first][t.second] + 1;
					visit[x][y] = 1;
					Q.push(pp(x, y));
				}

			}
		}
		int cnt2 = 0;
		//下方是記錄該點(bfs源點)到其它點的距離,建圖
		for (vector<pp>::iterator it2 = point.begin(); it2 != point.end(); it2++)
		{
			cnt2++;
			if (it == it2)continue;
			map_for_prime[cnt][cnt2] = map_for_prime[cnt2][cnt] = cost[it2->first][it2->second];
		}
	}

}

int prim()
{
	int ans = 0;
	int n = point.size();
	memset(visit_for_prime, 0, sizeof(visit_for_prime));
	for (int i = 2; i <= n; i++)
		d[i] = map_for_prime[1][i];
	d[1] = 0;
	for (int i = 2; i <= n; i++)
	{
		int t = -1;
		for (int j = 2; j <= n; j++)
		{
			if (!visit_for_prime[j])
				if (d[t] > d[j] || t == -1)
					t = j;
		}
		if (t == -1)break;
		visit_for_prime[t] = 1;
		ans += d[t];
		for (int j = 2; j <= n; j++)
			if (!visit_for_prime[j]) {
				d[j] = min(map_for_prime[t][j], d[j]);
			}
	}
	return ans;
}

int main()
{
	int t;
	scanf("%d", &t);
	while (t--)
	{
		memset(maps, 0, sizeof(maps));
		point.clear();
		scanf("%d%d", &m, &n);
		gets(str[0]);
		for (int i = 1; i <= n; i++)
		{
			gets(str[i]);
			for (int j = 0; j < m; j++) {
				if (str[i][j] == '#')maps[i][j + 1] = 1;
				else maps[i][j + 1] = 0;
				if (str[i][j] == 'A' || str[i][j] == 'S') {
					point.push_back(pp(i, j + 1));	//把每個點存起來
				}
			}

		}
		bfs();
		printf("%d\n", prim());
	}
	return 0;
}

11.POJ 1679 The Unique MST(次小生成樹,放生成樹專題寫)

12.HDU 1233 還是暢通工程

模板題

//hdu 1233
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 105, INF = 0x3f3f3f3f;
int maps[N][N],visit[N];
int d[N];
int n;

int prim()
{
	int ans = 0;
	for (int i = 2; i <= n; i++)
		d[i] = maps[1][i];
	d[1] = 0;
	for (int i = 2; i <= n; i++)
	{
		int t = -1;
		for (int j = 2; j <= n; j++)
		{
			if (!visit[j])
				if (d[j] < d[t] || t == -1)
					t = j;
		}
		visit[t] = 1;
		ans += d[t];
		for (int j = 2; j <= n; j++)
			if (!visit[j])
				d[j] = min(d[j], maps[t][j]);
	}
	return ans;
}

int main()
{
	while (~scanf("%d", &n),n)
	{
		memset(maps, 0x3f, sizeof(maps));
		memset(visit, 0, sizeof(visit));
		for (int i = 1; i <= n*(n-1)/2; i++)
			{
				int u,v,w;
				scanf("%d%d%d", &u,&v,&w);
				maps[u][v] = w;
				maps[v][u] = w;
			}
		printf("%d\n", prim());
	}
	return 0;
}

13.HDU 1301 Jungle Roads(與題目1重複)

14.HDU 1875 暢通工程再續

套用prim求最小生成樹,注意距離不在(10,1000)的不建邊。
感覺這題浮點數比大小巨坑,10.000000 <= len && len <= 1000.000001,少一個0都wa,去了杭電oj的discussion發現的
用相減大於>=eps(1e-6)也過不了,搞不懂。

//hdu 1875
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 105;
const double INF = 1e15;
const double eps = 1e-5;
int visit[N];
double d[N];
int n,m;
struct node {
	int x, y;
}maps[N];
double getDis(double x1, double y1, double x2, double y2)
{
	return sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2));
}
double prim()
{
	memset(visit, 0, sizeof(visit));
	for (int i = 1; i <= n; i++)
		d[i] = INF;
	double ans = 0;
	for (int i = 2; i <= n; i++) {
		double len = getDis(maps[1].x, maps[1].y, maps[i].x, maps[i].y);
		if (10.000000 <= len && len <= 1000.000001)	//距離不在(10,1000)的不建邊
			d[i] = len;
	}	
	d[1] = 0;
	for (int i = 2; i <= n; i++)
	{
		int t = -1;
		for (int j = 2; j <= n; j++)
		{
			if (!visit[j] && d[j]!=INF)
				if (d[t] > d[j]|| t == -1)
					t = j;
		}
		visit[t] = 1;
		if (!(10.000000 <= d[t] && d[t] <= 1000.000001))return -1;
		ans += d[t];
		for (int j = 2; j <= n; j++)
			if (!visit[j]) {
				double len = getDis(maps[t].x, maps[t].y, maps[j].x, maps[j].y);
				if(10.000000 <= len && len <= 1000.000001)
					d[j] = min(d[j], len);
			}
				
	}
	return ans;
}

int main()
{
	int t;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d", &n);
		for (int i = 1; i <= n; i++)
			scanf("%d%d", &maps[i].x,&maps[i].y);
		int flag = 0;
		double ans = prim();
		if (ans==-1)printf("oh!\n");
		else printf("%.1lf\n", ans*100);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章