08-图7 公路村村通(C)
很明显聪明的同学已经发现,这是一个稠密图,所以用邻接矩阵。可以很好的表达,比邻接表有优势,所以,采用邻接矩阵破题,
当然也可以用邻接表,仔细观察我的AC,会发现其实都一样,只是存储形式不一样罢了。
很明显,这是一个最小生成树问题,也可以认为贪心算法,接下来我将分别展示两个实现路径,第一个 prim 算法, 第二个kruskal算法。
第一个 prim 算法,哈哈,原来是我最开始搞错方向了,并未理解prim算法,这下也是让我印象深刻了。
测试点 提示 内存(KB) 用时(ms) 结果 得分 0 sample换数字,各种回路判断 192 4 答案正确
15 / 15 1 M<N-1,不可能有生成树 192 4 答案正确
2 / 2 2 M达到N-1,但是图不连通 192 4 答案正确
2 / 2 3 最大N和M,连通 4284 8 答案正确
5 / 5 4 最大N和M,不连通 4160 7 答案正确
6 / 6
第二个kruskal算法,实际应用很简单,果然馒头嚼碎也不见得好吸收,一点点啃食的过程,也是非常锻炼计算思维。
按道理来说,应该是kruskal算法快,但是也就一个O(N^2) 与O(NlogN),可以看出来在1000个数据点(最大N(1000),M(3000))下,区别还是有的。
测试点 提示 内存(KB) 用时(ms) 结果 得分 0 sample换数字,各种回路判断 188 4 答案正确
15 / 15 1 M<N-1,不可能有生成树 188 4 答案正确
2 / 2 2 M达到N-1,但是图不连通 188 4 答案正确
2 / 2 3 最大N和M,连通 4156 7 答案正确
5 / 5 4 最大N和M,不连通 4160 6 答案正确
6 / 6
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
输入格式:
输入数据包括城镇数目正整数 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
第一个 prim 算法,我的AC:
采用了,最小堆 + prim,.
#include<stdio.h> #include<stdlib.h> #include<stdbool.h> #define MaxVertex 1001 #define INFITY 100001 #define MinValue -1 typedef struct ENode *Edge; struct ENode{ int V1, V2; int Weight; }; typedef struct GNode *MGraph; struct GNode{ int Nv; int Ne; int G[MaxVertex][MaxVertex]; }; typedef struct Sign_Dist_VNode DistNode; struct Sign_Dist_VNode{ int V; int Weight; }; typedef struct Heap *MinHeap; struct Heap{ DistNode *Data; int Size; }; MGraph Build_Graph(); MGraph Init_Graph(); void Insert_Graph(MGraph M, Edge E); MinHeap Init_Heap(int N); void Creat_Heap(MGraph M, MinHeap H, int Vertex, bool* Visited, bool *collected); void Push_Heap(MinHeap H, int index, int Weight, bool *collected); void Counterpoise_Heap(MinHeap H, int index, int V, int Weight); bool IsEmpty_Heap(MinHeap H); DistNode Pop_Heap(MinHeap H, bool *collected); void Prim(MGraph M, MinHeap H, bool* Visited, int *dist); void Print_Result(int N, int *dist, bool* Visited); int main() { MGraph M; MinHeap H; int *dist; bool *Visited; M = Build_Graph(); H = Init_Heap(M ->Nv); dist = (int*)malloc(sizeof(int) * M ->Nv); Visited = (bool*)calloc(M ->Nv, sizeof(bool)); Prim(M, H, Visited, dist); Print_Result(M ->Nv, dist, Visited); return 0; } void Prim(MGraph M, MinHeap H, bool* Visited, int *dist) { int j; bool *collected; collected = (bool*)calloc(M ->Nv, sizeof(bool)); for(j = 1; j < M ->Nv; j++){ dist[j] = M ->G[0][j]; } DistNode Data; Creat_Heap(M, H, 0, Visited, collected); Visited[0] = true; dist[0] = 0; while(Data = Pop_Heap(H, collected), Data.Weight > 0){ Visited[Data.V] = true; for(j = 0; j < M ->Nv; j++){ if(!Visited[j] && M ->G[Data.V][j] < INFITY && (dist[j] > M ->G[Data.V][j])){ dist[j] = M ->G[Data.V][j]; Push_Heap(H, j, dist[j], collected); //有人会问,这样不会产生数据重复吗,注意Visited,遵循一次访问原则。 } } } } void Print_Result(int N, int *dist, bool* Visited) { int i, cost = 0; for(i = 0; i < N; i++){ if(!Visited[i]){ printf("-1\n"); return ; } cost += dist[i]; } printf("%d\n", cost); return ; } MGraph Build_Graph() { MGraph M; Edge E; M = Init_Graph(); E = (Edge)malloc(sizeof(struct ENode)); for(int i = 0; i < M ->Ne; i++){ scanf("%d %d %d", &E ->V1, &E ->V2, &E ->Weight); E ->V1 --; E ->V2 --; Insert_Graph(M, E); } return M; } MGraph Init_Graph() { MGraph M; M = (MGraph)malloc(sizeof(struct GNode)); scanf("%d %d", &M ->Nv, &M ->Ne); for(int i = 0; i < (M ->Nv); i++){ for(int j = 0; j < (M ->Nv); j++){ M ->G[i][j] = INFITY; } } return M; } void Insert_Graph(MGraph M, Edge E) { M ->G[E ->V1][E ->V2] = E ->Weight; M ->G[E ->V2][E ->V1] = E ->Weight; } void Creat_Heap(MGraph M, MinHeap H, int Vertex, bool* Visited, bool *collected) { for(int j = 0; j < M ->Nv; j++){ if(M ->G[Vertex][j] < INFITY && !Visited[j]){ Push_Heap(H, j, M ->G[Vertex][j], collected); } } } MinHeap Init_Heap(int N) { MinHeap H; H = (MinHeap)malloc(sizeof(struct Heap)); H ->Data = (DistNode*)malloc(sizeof(DistNode) * (N + 1)); H ->Data[0].Weight = MinValue; H ->Size = 0; return H; } void Push_Heap(MinHeap H, int index, int Weight, bool *collected) { int i; if(collected[index]){ for(i = 1; i <= H ->Size; i++){ if(index == H ->Data[i].V){ H ->Data[i].Weight = Weight; Counterpoise_Heap(H, index, i, Weight); return ; } } }else{ i = ++(H ->Size); Counterpoise_Heap(H, index, i, Weight); collected[index] = true; return ; } } void Counterpoise_Heap(MinHeap H, int index, int i, int Weight) { for(; Weight < H ->Data[i/2].Weight; i /= 2){ H ->Data[i].Weight = H ->Data[i/2].Weight; H ->Data[i].V = H ->Data[i/2].V; } H ->Data[i].Weight = Weight; H ->Data[i].V = index; return ; } bool IsEmpty_Heap(MinHeap H) { return (H ->Size == 0); } DistNode Pop_Heap(MinHeap H, bool *collected) { DistNode Min, Temp; int Parent, Child; if(IsEmpty_Heap(H)){ return H ->Data[0]; } Min = H ->Data[1]; Temp = H ->Data[H ->Size --]; collected[Min.V] = false; for(Parent = 1; Parent *2 < H ->Size; Parent = Child){ Child = Parent * 2; if(H ->Data[Child].Weight > H ->Data[Child + 1].Weight){ Child++; } if(Temp.Weight <= H ->Data[Child].Weight){ break; }else{ H ->Data[Parent].Weight = H ->Data[Child].Weight; H ->Data[Parent].V = H ->Data[Child].V; } } H ->Data[Parent].Weight = Temp.Weight; H ->Data[Parent].V = Temp.V; return Min; }
第二个kruskal算法, 最小堆+并查树+Kruskal。
#include<stdio.h> #include<stdlib.h> #include<stdbool.h> #define MaxVertex 1001 #define INFITY 100001 #define MinValue -1 typedef struct ENode Edge; struct ENode{ int V1, V2; int Weight; }; typedef struct GNode *MGraph; struct GNode{ int Nv; int Ne; int G[MaxVertex][MaxVertex]; }; typedef struct Heap *MinHeap; struct Heap{ Edge *Data; int Size; }; //便于并查树算法理解,有心吧。 typedef struct UnionFind *UF; struct UnionFind{ int Parent; }; MGraph Build_Graph(); MGraph Init_Graph(); void Insert_Graph(MGraph M, Edge E); MinHeap Init_Heap(int N); MinHeap Creat_Heap(MGraph M); void Push_Heap(MinHeap H, int V1, int V2, int Weight); bool IsEmpty_Heap(MinHeap H); Edge Pop_Heap(MinHeap H); UF Init_UnionFind(int VertexNum); void Union_Value(UF T, int V1, int V2); int Find_Root(UF T, int V); bool Check_Loop(UF T, int V1, int V2); void Kruskal(MGraph M, MinHeap H, UF T, bool *Visited, int *dist); int main() { MGraph M; MinHeap H; UF T; int *dist; bool *Visited; M = Build_Graph(); H = Creat_Heap(M); T = Init_UnionFind(M ->Nv); dist = (int*)malloc(sizeof(int) * M ->Nv); Visited = (bool*)calloc(M ->Nv, sizeof(bool)); Kruskal(M, H, T, Visited, dist); return 0; } void Kruskal(MGraph M, MinHeap H, UF T, bool *Visited, int *dist) { Edge Data; int i = 1; int cost = 0; while(Data = Pop_Heap(H), (Data.Weight > 0 && i < M ->Nv)){ if(!Check_Loop(T, Data.V1, Data.V2)){ Union_Value(T, Data.V1, Data.V2); cost += Data.Weight; i++; } } if(i != M ->Nv){ printf("-1\n"); }else{ printf("%d\n", cost); } } MGraph Build_Graph() { MGraph M; Edge E; M = Init_Graph(); for(int i = 0; i < M ->Ne; i++){ scanf("%d %d %d", &E.V1, &E.V2, &E.Weight); E.V1 --; E.V2 --; Insert_Graph(M, E); } return M; } MGraph Init_Graph() { MGraph M; M = (MGraph)malloc(sizeof(struct GNode)); scanf("%d %d", &M ->Nv, &M ->Ne); for(int i = 0; i < (M ->Nv); i++){ for(int j = 0; j < (M ->Nv); j++){ M ->G[i][j] = INFITY; } } return M; } void Insert_Graph(MGraph M, Edge E) { M ->G[E.V1][E.V2] = E.Weight; M ->G[E.V2][E.V1] = E.Weight; } MinHeap Creat_Heap(MGraph M) { MinHeap H; H = Init_Heap(M ->Ne); for(int i = 0; i < M ->Nv; i++) for(int j = i + 1; j < M ->Nv; j++){ if(M ->G[i][j] < INFITY){ Push_Heap(H, i, j, M ->G[i][j]); } } return H; } MinHeap Init_Heap(int N) { MinHeap H; H = (MinHeap)malloc(sizeof(struct Heap)); H ->Data = (Edge*)malloc(sizeof(Edge) * (N + 1)); H ->Data[0].Weight = MinValue; H ->Size = 0; return H; } void Push_Heap(MinHeap H, int V1, int V2, int Weight) { int i; i = ++(H ->Size); for(; Weight < H ->Data[i/2].Weight; i /= 2){ H ->Data[i].Weight = H ->Data[i/2].Weight; H ->Data[i].V1 = H ->Data[i/2].V1; H ->Data[i].V2 = H ->Data[i/2].V2; } H ->Data[i].Weight = Weight; H ->Data[i].V1 = V1; H ->Data[i].V2 = V2; return ; } bool IsEmpty_Heap(MinHeap H) { return (H ->Size == 0); } Edge Pop_Heap(MinHeap H) { Edge Min, Temp; int Parent, Child; if(IsEmpty_Heap(H)){ return H ->Data[0]; } Min = H ->Data[1]; Temp = H ->Data[H ->Size --]; for(Parent = 1; Parent *2 < H ->Size; Parent = Child){ Child = Parent * 2; if(H ->Data[Child].Weight > H ->Data[Child + 1].Weight){ Child++; } if(Temp.Weight <= H ->Data[Child].Weight){ break; }else{ H ->Data[Parent].Weight = H ->Data[Child].Weight; H ->Data[Parent].V1 = H ->Data[Child].V1; H ->Data[Parent].V2 = H ->Data[Child].V2; } } H ->Data[Parent].Weight = Temp.Weight; H ->Data[Parent].V1 = Temp.V1; H ->Data[Parent].V2 = Temp.V2; return Min; } UF Init_UnionFind(int VertexNum) { UF T; T = (UF)malloc(sizeof(struct UnionFind) * (VertexNum + 1)); for(int i = 0; i < VertexNum; i++){ T[i].Parent = -1; } return T; } void Union_Value(UF T, int V1, int V2) { int Root1, Root2; Root1 = Find_Root(T, V1); Root2 = Find_Root(T, V2); if(T[Root1].Parent < T[Root2].Parent){ T[Root1].Parent += T[Root2].Parent; T[Root2].Parent = Root1; }else{ T[Root2].Parent += T[Root1].Parent; T[Root1].Parent = Root2; } } int Find_Root(UF T, int V) { if(T[V].Parent < 0){ return V; } return Find_Root(T, T[V].Parent); } bool Check_Loop(UF T, int V1, int V2) { if(Find_Root(T, V1) == Find_Root(T, V2)){ return true; }else{ return false; } }
2024.9.6-错误借鉴,哈哈,我竟然,好吧,就是没有好好听课,不是最短路,而是最小边,之前的样例我感觉只是运气吧。
测试点 提示 内存(KB) 用时(ms) 结果 得分 0 sample换数字,各种回路判断 184 4 答案正确
15 / 15 1 M<N-1,不可能有生成树 188 4 答案正确
2 / 2 2 M达到N-1,但是图不连通 316 4 答案正确
2 / 2 3 最大N和M,连通 4156 8 答案错误
0 / 5 4 最大N和M,不连通 4156 7 答案正确
6 / 6 void Prim(MGraph M, MinHeap H, bool* Visited, int *dist) { int j; bool *collected; collected = (bool*)calloc(M ->Nv, sizeof(bool)); for(j = 0; j < M ->Nv; j++){ dist[j] = INFITY; } DistNode Data; Creat_Heap(M, H, 0, Visited, collected); Visited[0] = true; dist[0] = 0; while(Data = Pop_Heap(H, collected), Data.Weight > 0){ if(!Visited[Data.V] && dist[Data.V] > Data.Weight){ dist[Data.V] = Data.Weight; } Visited[Data.V] = true; for(j = 0; j < M ->Nv; j++){ if(!Visited[j] && M ->G[Data.V][j] < INFITY && (dist[j] > dist[Data.V] + M ->G[Data.V][j])){ dist[j] = M ->G[Data.V][j]; Push_Heap(H, j, dist[j], collected); //有人会问,这样不会产生数据重复吗,注意Visited,遵循一次访问原则。 } } } }