《数据结构》08-图7 公路村村通(最小生成树问题)

数据结构》08-图7 公路村村通(最小生成树问)

题目
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。

输入格式:
输入数据包括城镇数目正整数N(≤1000)和候选道路数目M(≤3N);随后的M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个城镇的编号以及该道路改建的预算成本。为简单起见,城镇从1到N编号。

输出格式:
输出村村通需要的最低成本。如果输入数据不足以保证畅通,则输出−1,表示需要建设更多公路。

输入样例:

6 15
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2
2 3 4
2 4 6
2 5 2
2 6 6
3 4 6
3 5 1
3 6 1
4 5 10
4 6 8
5 6 3

输出样例:

12
—————————————————————————

方法一:Kruskal算法

核心–不停地找权重最小的变合在一起但需要注意的是 不能构成回路 如何找最小权重所在的边,可以靠最小堆来解决,如何判断是否构成回路呢,可以用并查集看是否属于同一棵树,这可以参考之前的并查集的blog

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
#define maxsize 1001
#define inf 99999999999
int G[maxsize][maxsize];
int d[maxsize];
int parent[maxsize];
vector<int>v;
int  n, m, a1, a2;
struct edge
{
	int u, v;
	int cost;
}e[maxsize];
bool cmp(edge a, edge b)
{
	return a.cost < b.cost;
}
int father[maxsize];
int findfather(int i)
{
	int a = i;
	while (father[i] != i)
	{
		i = father[i];
	}
	while (father[a] != a)
	{
		int z = a;
		a = father[a];
		father[a] = i;
	}
	return i;
}
int Krus(int n, int m)
{
	int ans = 0, num_edge = 0;
	for (int i = 0; i < n; i++)
	{
		father[i] = i;
	}
	sort(e, e + m, cmp);
	for (int i = 0; i < m; i++)
	{
		int fau = findfather(e[i].u);
		int fav = findfather(e[i].v);
		if (fau != fav)
		{
			father[fau] = fav;
			ans += e[i].cost;
			num_edge++;
			if (num_edge == n - 1) break;
		}
	}
	if (num_edge != n - 1) return -1;
	else return ans;
}
int main()
{
	cin >> n >> m;
	for (int i = 0; i < m; i++)
	{
		cin >> e[i].u >> e[i].v >> e[i].cost;
	}
	int ans = Krus(n, m);
	cout << ans << endl;

}

—————————————————————————

方法二

prim算法,类似于DJ算法,贪心思想,找到临近最短的就更新

#include<iostream>
#include<vector>
using namespace std;
#define inf 100000
#define maxsize 1001
int g[maxsize][maxsize];
int parent[maxsize];
int dist[maxsize];
int n;
int m;
int sum;
using namespace std;
vector<int>mst;
void build()
{
	int v1, v2;
	int w;
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			g[i][j] = 0;
		}
		dist[i]= inf;
		parent[i] = -1;
	}
	for (int i = 0; i < m; i++) {
		cin >> v1 >> v2 >> w;
		g[v1][v2] = w;
		g[v2][v1] = w;
	}
}

void init(int x)       //初始化第一个点和到周围点的距离
{
	dist[x] = 0;
	mst.push_back(x);
	for (int i = 1; i <= n; i++) {
		if (g[x][i]) {
			dist[i] = g[x][i];
			parent[i] = x;
		}
	}
}

int findmin()      //找点周围路径最短的点
{
	int min = inf;
	int xb = -1;
	for (int i = 1; i <= n; i++) {
		if (dist[i] && dist[i] < min) {
			min = dist[i];
			xb = i;
		}
	}
	return xb;
}
void prim(int s)
{
	init(s);
	while (1) {
		int v = findmin();
		if (v == -1)       //不存在这样的点
			break;
		sum += dist[v];
		dist[v] = 0;
		mst.push_back(v);
		for (int i = 1; i <= n; i++) {
			if (g[v][i] && g[v][i] < dist[i]) {
				dist[i] = g[v][i];
				parent[i] = v;
			}
		}
	}
}
int main()
{
	build();
	prim(1);        //哪个点开始都无所谓的
	if (mst.size() == n)
		cout << sum;
	else
		cout << -1;
	return 0;
}
#include<iostream>
#include<vector>
using namespace std;
#define maxsize 1001
#define inf 99999999999
int G[maxsize][maxsize];
int d[maxsize];
int parent[maxsize];
vector<int>v;
int  n, m, a1, a2;
int findmin()
{
	int min = inf;
	int xb = -1;
	for (int i = 1; i <= n; i++)
	{
		if (d[i]&&d[i] < min)
		{
			min = d[i];
			xb = i;
		}
	}
	return xb;
}
int main()
{
	cin >> n >> m;
	int w;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			G[i][j] = 0;
		}
		d[i] = inf;
	}
	for (int i = 0; i < m; i++)
	{
		cin >> a1 >> a2 >> w;
		G[a2][a1] = G[a1][a2] = w;
	}
	d[1] = 0;
	v.push_back(1);
	for (int i = 1; i <= n; i++)
	{
		if (G[1][i])
		{
			d[i] = G[1][i];
		}
	}
	int sum = 0;
	while (1)
	{
		int q = findmin();
		if (q == -1)
			break;
		sum += d[q];
		d[q] = 0;
		v.push_back(q);
		for (int i = 1; i <= n; i++)
		{
			if (G[q][i]&&G[q][i] < d[i])
			{
				d[i] = G[q][i];
			}
		}
	}
	if (v.size() == n)
		cout << sum << endl;
	else
		cout << "-1" << endl;
}

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值