最小生成树的prim算法 边的权值为啥不能为负值
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最小生成树的prim算法 边的权值为啥不能为负值相关的知识,希望对你有一定的参考价值。
最小生成树的prim算法 边的权值为什么不能为负值。这个跟实际意义没关系。从算法里面看不出为什么不能为负,求达人解惑
理论上是可以的,但是0的意义就变了,必须另取一个数来表示最小代价 参考技术A 不是吧,应该是求最短路径的Dijkstra算法才有这个要求Prim算法和Kruskal算法(图论中的最小生成树算法)
最小生成树在一个图中可以有多个,但是如果一个图中边的权值互不相同的话,那么最小生成树只可能存在一个,用反证法很容易就证明出来了。
当然最小生成树也是一个图中包含所有节点的权值和最低的子图。
在一个图中权值最小的那个边一定在最小生成树中,如果一个图包含环,环中权值最大的边一定不在最小生成树中,还有就是连接图的任意两个划分的边中权值最短的那一条一定在最小生成树中。
下面介绍两个算法。
Prim算法
Prim算法就是以任意一个点为源点,将所有点分为两组,一组是已经在最小生成树上的点,另一组是还未在最小生成树上的点,最初只有源点在最小生成树上。图中一定存在这样的边:它们的一个顶点在最小生成树上,另一点不在最小生成树上,将这些边中权值最短的那一条以及所连接的点加入最小生成树,继续循环直至所有点都在最小生成树上为止。在具体实现中,可以用一个包含所有节点的辅助数组来记录,这个数组的每个元素包含该点到最小生成树的距离最短的边的权值和这条边的另一个端点(也就是已经在树上的那个点),如果这个点已经在最小生成树上了,权值就为0,这样每次往最小生成树上新加一个点的时候,就去检查所有与它相邻的点,如果那些相邻的点到新加的的距离小于到原先树中点的距离的话,就更新那些点在辅助数组中的信息。
int Prim(struct Graph *G, struct Vertex vertex_list[ ], int S){ int i, j, min_cost, tmp_index, sum = 0; for(i = 0; i < G->vertex_num; i++){ if(S != i){ vertex_list[i].adjacent_vertex = S; vertex_list[i].low_cost = G->map[S][i]; } else{ vertex_list[i].adjacent_vertex = -1; vertex_list[i].low_cost = 0; } } for(j = 1; j < G->vertex_num; j++){ min_cost = Infinity; for(i = 0; i < G->vertex_num; i++){ if(0 != vertex_list[i].low_cost&&min_cost > vertex_list[i].low_cost){ min_cost = vertex_list[i].low_cost; tmp_index = i; } } sum += min_cost; vertex_list[tmp_index].low_cost = 0; for(i = 0; i < G->vertex_num; i++){ if(0 != vertex_list[i].low_cost&&vertex_list[i].low_cost > G->map[tmp_index][i]){ vertex_list[i].adjacent_vertex = tmp_index; vertex_list[i].low_cost = G->map[tmp_index][i]; } } } return sum; }
Kruskal算法
Kruskal算法就是将所有边按权值从小到大加入最小生成树中,并且保证不形成环,确定不形成环的方式类似于并查集。
int Kruskal(struct Graph *G){ int index = 0, i, j, sum = 0, tmp, tmp_1, tmp_2; int vertex_list[G->vertex_num]; struct Edge edge_list[G->edge_num]; for(i = 0; i < G->vertex_num; i++){ vertex_list[i] = i; } for(i = 0; i < G->vertex_num; i++){ for(j = i; j < G->vertex_num; j++){ if(Infinity != G->map[i][j]){ edge_list[index].start = i; edge_list[index].end = j; edge_list[index].weight = G->map[i][j]; index++; } } } qsort(edge_list, G->edge_num, sizeof(struct Edge), comp); for(i = 0; i < G->edge_num; i++){ if(vertex_list[edge_list[i].start] != vertex_list[edge_list[i].end]){ for(j = 0; j < G->vertex_num; j++){ if(vertex_list[j] == vertex_list[edge_list[i].end]){ vertex_list[j] = vertex_list[edge_list[i].start]; } } sum += edge_list[i].weight; printf("%d %d\n", edge_list[i].start, edge_list[i].end); } } return sum; }
下面是完整代码,我也没怎么测过。
// // main.c // Smallest Spanning Tree // // Created by 余南龙 on 2016/11/27. // Copyright ? 2016年 余南龙. All rights reserved. // #include <stdio.h> #include <stdlib.h> #define MAXV 100 #define Infinity 10000000 struct Vertex{ int adjacent_vertex; int low_cost; }; struct Edge{ int start; int end; int weight; }; struct Graph{ int map[MAXV][MAXV]; int edge_num; int vertex_num; }; int comp(const void *a, const void *b){ struct Edge *pa = (struct Edge *)a; struct Edge *pb = (struct Edge *)b; if(pa->weight < pb->weight){ return -1; } else{ return 1; } } int Prim(struct Graph *G, struct Vertex vertex_list[ ], int S){ int i, j, min_cost, tmp_index, sum = 0; for(i = 0; i < G->vertex_num; i++){ if(S != i){ vertex_list[i].adjacent_vertex = S; vertex_list[i].low_cost = G->map[S][i]; } else{ vertex_list[i].adjacent_vertex = -1; vertex_list[i].low_cost = 0; } } for(j = 1; j < G->vertex_num; j++){ min_cost = Infinity; for(i = 0; i < G->vertex_num; i++){ if(0 != vertex_list[i].low_cost&&min_cost > vertex_list[i].low_cost){ min_cost = vertex_list[i].low_cost; tmp_index = i; } } sum += min_cost; vertex_list[tmp_index].low_cost = 0; for(i = 0; i < G->vertex_num; i++){ if(0 != vertex_list[i].low_cost&&vertex_list[i].low_cost > G->map[tmp_index][i]){ vertex_list[i].adjacent_vertex = tmp_index; vertex_list[i].low_cost = G->map[tmp_index][i]; } } } return sum; } int Kruskal(struct Graph *G){ int index = 0, i, j, sum = 0, tmp, tmp_1, tmp_2; int vertex_list[G->vertex_num]; struct Edge edge_list[G->edge_num]; for(i = 0; i < G->vertex_num; i++){ vertex_list[i] = i; } for(i = 0; i < G->vertex_num; i++){ for(j = i; j < G->vertex_num; j++){ if(Infinity != G->map[i][j]){ edge_list[index].start = i; edge_list[index].end = j; edge_list[index].weight = G->map[i][j]; index++; } } } qsort(edge_list, G->edge_num, sizeof(struct Edge), comp); for(i = 0; i < G->edge_num; i++){ if(vertex_list[edge_list[i].start] != vertex_list[edge_list[i].end]){ for(j = 0; j < G->vertex_num; j++){ if(vertex_list[j] == vertex_list[edge_list[i].end]){ vertex_list[j] = vertex_list[edge_list[i].start]; } } sum += edge_list[i].weight; printf("%d %d\n", edge_list[i].start, edge_list[i].end); } } return sum; } void Init(struct Graph *G){ int i, j, start, end, weight; scanf("%d%d", &(G->edge_num), &(G->vertex_num)); for(i = 0; i < G->vertex_num; i++){ for(j = 0; j < G->vertex_num; j++){ G->map[i][j] = Infinity; } } for(i = 0; i < G->edge_num; i++){ scanf("%d%d%d", &start, &end, &weight); G->map[start][end] = G->map[end][start] = weight; } } void Output(struct Vertex vertex_list[ ], int size){ int i; for(i = 0; i < size; i++){ printf("%d %d\n", i, vertex_list[i].adjacent_vertex); } } int main(){ struct Graph G; struct Vertex *vertex_list; int sum; Init(&G); vertex_list = (struct Vertex*)malloc(sizeof(struct Vertex) * G.vertex_num); sum = Prim(&G, vertex_list, 3); printf("%d\n", sum); Output(vertex_list, G.vertex_num); sum = Kruskal(&G); printf("%d\n", sum); }
以上是关于最小生成树的prim算法 边的权值为啥不能为负值的主要内容,如果未能解决你的问题,请参考以下文章
bellman-ford算法求单源点最短路径边的权值为啥可以为负值?遍历代价存在负值吗?
算法判断题:一个图中边的权值都平方后之前的最小生成树仍是这个图的最小生成树,对不对?