【问题描述】
已知含有n个顶点的带权连通无向图,采用邻接矩阵存储,邻接矩阵以三元组的形式给出,只给出不包括主对角线元素在内的下三角形部分的元素,且不包括不相邻的顶点对。请采用Prim算法,求该连通图从1号顶点出发的最小生成树的权值之和。
【输入形式】
第一行给出结点个数n和三元组的个数count,以下每行给出一个三元组,数之间用空格隔开。(注意这里顶点的序号是从1到n,而不是0到n-1,程序里要小心!)
【输出形式】
求解的最小生成树的各条边、边的权值之和
【样例输入】
5 8
2 1 7
3 1 6
3 2 8
4 1 9
4 2 4
4 3 6
5 2 4
5 4 2
【样例输出】
1-3:6
3-4:6
4-5:2
4-2:4
18
【样例说明】
权值是正整数,可能很大,但不需要考虑整型溢出问题
解法:
先将三元组转化为邻接矩阵,无向图的邻接矩阵为对称矩阵
m[i][j]=k代表从第i号结点到第j号结点的权值为k
∞ | 7 | 6 | 9 | ∞ |
7 | ∞ | 8 | 4 | 4 |
6 | 8 | ∞ | 6 | ∞ |
9 | 4 | 6 | ∞ | 4 |
∞ | 4 | ∞ | 4 | ∞ |
tag代表是否被并入U集合,neighbour代表邻接顶点的编号,weight代表两个顶点间的权重
每一趟都将所有tag为0的点与选中顶点的权重和表里原来存放的weight作比较,如果前者的值较小,则修改表里的值(红色代表被修改的值,蓝色代表被选中的顶点),如果两个值相同,不要修改。
修改操作完成之后,找到所有tag为0中的点对应的weight值最小的那个点,将他的tag置为1,也就是把他所对应的线并入最小生成树。
第一趟:
顶点编号 | 1 | 2 | 3 | 4 | 5 |
tag | 1 | 0 | 0 | 0 | 0 |
neighbour | 1 | 1 | 1 | 1 | |
weight | ∞ | ∞ | ∞ | ∞ |
第二趟:
顶点编号 | 1 | 2 | 3 | 4 | 5 |
tag | 1 | 0 | 0 | 0 | 0 |
neighbour | 1 | 1 | 1 | 1 | |
weight | 7 | 6 | 9 | ∞ |
第三趟:
顶点编号 | 1 | 2 | 3 | 4 | 5 |
tag | 1 | 0 | 1 | 0 | 0 |
neighbour | 1 | 1 | 3 | 1 | |
weight | 7 | 6 | 6 | ∞ |
第四趟:
顶点编号 | 1 | 2 | 3 | 4 | 5 |
tag | 1 | 0 | 1 | 1 | 0 |
neighbour | 4 | 1 | 3 | 4 | |
weight | 4 | 6 | 6 | 2 |
第五趟:
顶点编号 | 1 | 2 | 3 | 4 | 5 |
tag | 1 | 0 | 1 | 1 | 1 |
neighbour | 4 | 1 | 3 | 4 | |
weight | 4 | 6 | 6 | 2 |
第六趟:
顶点编号 | 1 | 2 | 3 | 4 | 5 |
tag | 1 | 1 | 1 | 1 | 1 |
neighbour | 4 | 1 | 3 | 4 | |
weight | 4 | 6 | 6 | 2 |
此时选中了n-1条边,循环结束。
表格里存放的便是最小生成树。
但是,题目这个遍历的顺序是dfs的顺序,所以我把这棵树又存进了邻接矩阵
0 | 0 | 6 | 0 | 0 |
0 | 0 | 0 | 4 | 0 |
6 | 0 | 0 | 6 | 0 |
0 | 4 | 6 | 0 | 2 |
0 | 0 | 0 | 2 | 0 |
这样dfs比较方便
#include
using namespace std;
const int N = 100;
const int Max = 99999;
class MST
{
public:
int tag,weight,nb;
MST():tag(0),weight(Max){}
};
void input(int m[][N], int n, int count)//初始化邻接矩阵
{
for (int i = 0; i <= n; i++)
{
for (int j = 0; j <= n; j++)
{
m[i][j] = Max;
}
}
for (int i = 0; i < count; i++)
{
int a, b, c;
cin >> a >> b >> c;
//cout << a << ' ' << b << ' ' << c << endl;
m[a][b] = c;
m[b][a] = c;
}
}
void prim(MST tr[],int m[][N], int n)
{
int sum = 0, choice=1, wmin = Max, mmin;
tr[1].tag = 1;
while (sum < n-1)
{
wmin = Max;
for (int i = 2; i <= n; i++)//修改
{
if (tr[i].tag == 0)
{
if (m[i][choice] < tr[i].weight)
{
tr[i].weight = m[i][choice];
tr[i].nb = choice;
}
if (tr[i].weight < wmin)
{
wmin = tr[i].weight;
mmin = i;
}
}
}
tr[mmin].tag = 1;
choice = mmin;
sum++;
}
}
void MSTtrans(MST* tr, int m[][N],int n)
{
for (int i = 1; i <= n; i++)
{
for (int j = 0; j <= n; j++)
{
m[i][j] = 0;
}
}
for (int i = 2; i <= n; i++)
{
m[i][tr[i].nb] = tr[i].weight;
m[tr[i].nb][i] = tr[i].weight;
}
}
void dfs(int m[][N],int visited[][N], int i, int n)
{
for (int j = 1; j <= n; j++)
{
if (m[i][j] != 0 && visited[i][j] == 0)
{
cout << i << '-' << j << ':' << m[i][j] << endl;
visited[i][j] = 1; visited[j][i] = 1;
dfs(m, visited, j, n);
}
}
}
int main()
{
int m[N][N]; int visited[N][N] = {0};
int n,count;
cin >> n >> count;
//cout << n <<' '<< count<
更新堆优化的版本
#include
#include
using namespace std;
const int inf = 1e9 + 7;
int n, m, dis[51], vis[51], ans = 0;
int head[51], cnt = 0;
struct Node {
int pos, w;
Node(int p, int wt) :pos(p), w(wt) {}
bool operator<(const Node& a)const {
return w > a.w;
}
};
struct Edge {
int to, next, w;
}e[101];
void add_edge(int u, int v, int w) {
++cnt;
e[cnt].to = v, e[cnt].w = w, e[cnt].next = head[u];
head[u] = cnt;
}
void prim() {
fill(dis + 1, dis + 1 + n, inf);
priority_queue q;
q.push({ 1,0 });
while (q.size()) {
Node tmp = q.top(); q.pop();
if (!vis[tmp.pos]) {
vis[tmp.pos] = 1; ans += tmp.w;
for (int i = head[tmp.pos]; i; i = e[i].next) {
int v = e[i].to, w = e[i].w;
if (!vis[v]) {
if (w < dis[v]) {
dis[v] = w;
q.push({ v,dis[v] });
}
}
}
}
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
add_edge(u, v, w);
add_edge(v, u, w);
}
prim();
cout << ans;
return 0;
}