公路村村通 (30分)【C语言】Prim算法 和 Kruskal算法
目录
- 题目:
- 输入格式
- 输出格式
- 输入样例
- 输出样例
- 问题分析
- 最小生成树的特点
- 算法
- Prim算法
- 代码实现
- Prim函数:小树变大的过程中记录fee(费用和)和count(已收集的边数)
- FindMinDistVertex函数:寻找距collected集合最近的结点
- 其他函数:邻接矩阵基础操作
- Kruskal算法
- 代码实现
- Kruskal函数
- 并查集函数
- 最小堆函数
习题讲解视频
题目:
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
输入格式
输入数据包括城镇数目正整数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
问题分析
本题的大意是将所有村落连在一起花费最少钱,属于典型的最小生成树问题
最小生成树的特点
- 无回路
- 边数=结点数-1
- 所有结点在一个连通集
- 权重和最小
算法
Prim算法
- 通过正向构造collected集合满足无回路和权重和最小的条件
- 检查条件一(连通集结束时是否所有结点已被包含)和条件二(边数是否满足结点数小一)结束算法
说明:类似Dijkstra算法,按照递增的方式构造集合collected,但是prim算法中dist的值不再是距源点的距离,而是距集合collected的距离(本题dist是指修一条通向集合collected的路,最少花费多少钱)
代码实现
int main()
{ int N,M;scanf("%d %d",&N,&M);Graph G=CreateGraph(N);BuildGraph(G,M); if(M<N-1){printf("-1");}else{Prim(G);}return 0;
}
Prim函数:小树变大的过程中记录fee(费用和)和count(已收集的边数)
void Prim(Graph G)
{int fee=0;//从结点1开始让树长大,fee记录费用 int count=0;//统计collected集合中已经收录结点个数 int dist[VERTEXMAXNUM];//未收录的任意一点到collected集合的最短路径长度 bool collected[VERTEXMAXNUM];int i;{for(i=1;i<=G->VertexNum ;i++){dist[i]=Infinite;collected[i]=false;} for(i=1;i<=G->VertexNum ;i++){if(G->Matrix[1][i] <Infinite){dist[i]=G->Matrix[1][i];}}}collected[1]=true;dist[1]=0;int minDistVertex=-1;while(1){minDistVertex=FindMinDistVertex(dist,collected,G->VertexNum );if(minDistVertex==-1||count==G->VertexNum-1)break;collected[minDistVertex]=true;fee+=dist[minDistVertex];dist[minDistVertex]=0;count++;for(i=1;i<=G->VertexNum;i++){if(collected[i]==true)continue;if(G->Matrix[minDistVertex][i]<dist[i]){dist[i]=G->Matrix[minDistVertex][i];}}}if(count<G->VertexNum-1){printf("-1");}else{printf("%d",fee);}
}
FindMinDistVertex函数:寻找距collected集合最近的结点
int FindMinDistVertex(int dist[],bool collected[],int N)
{int i;int MinDist=Infinite;int vertex=-1;for(i=1;i<=N;i++){if(MinDist>dist[i]&&(collected[i]==false)){MinDist=dist[i];vertex=i;}}return vertex;
}
其他函数:邻接矩阵基础操作
#define Infinite 65535
#define VERTEXMAXNUM 1001
typedef struct GNode* Graph;
struct GNode{int VertexNum;int EdgeNum;int Matrix[VERTEXMAXNUM][VERTEXMAXNUM];
};
typedef struct ENode* Edge;
struct ENode{int V;int W;int Weight;
};
Graph CreateGraph(int N)
{Graph G=(Graph)malloc(sizeof(struct GNode));G->VertexNum=N;int i=0,j=0;for(i=1;i<=N;i++){for(j=1;j<=N;j++){G->Matrix[i][j]=Infinite;}}return G;
}
void InsertEdge(Edge L,Graph G)
{G->Matrix [L->V ][L->W ]=L->Weight ;G->Matrix [L->W ][L->V ]=L->Weight;
}
void BuildGraph(Graph G,int M)
{int i=0;G->EdgeNum=M;Edge L=(Edge)malloc(sizeof(struct ENode));for(i=0;i<M;i++){scanf("%d %d %d",&(L->V),&(L->W ),&(L->Weight ));InsertEdge(L,G);}free(L);
}
Kruskal算法
- 根据边创建最小堆H
- 在while计数循环中弹出最小边Node,检查边Node的两个结点是否能构成环(即是否两个结点已经在一个集合里,并查集处理),如果不能构成环则记录此边,否则抛弃。
- 当while循环中边计数器count==总结点数-1时,停止(或者最小堆中不能在弹出元素)
代码实现
int main()
{int N,M;scanf("%d %d",&N,&M);Heap H=CreateHeap(M);BuildMinheap(H); if(M<N-1){printf("-1");}else{Kruskal(H,N);}return 0;
}
Kruskal函数
说明:在while计数循环中弹出最小边Node,检查边Node的两个结点是否能构成环(即是否两个结点已经在一个集合里,并查集处理),如果不能构成环则记录此边,否则抛弃
void Kruskal(MinHeap H,int N)
{int fee=0;//fee记录费用 int count=0;//统计已经收录边数 int *SetType=(int*)malloc(sizeof(int)*(N+1));//并查集用的集合int i;for(i=1;i<=N;i++){SetType[i]=-1;}ElementType Node;//每次从最小堆中弹出的元素int V,W;while(count<N-1){Node=DeleteMinHeap(H); if(Node.Weight==-1)break;//最小堆中已无元素V=Node.V;W=Node.W;if(!IsSameSet(V,W,SetType)){//检查边的两个结点是否在一个环中fee+=Node.Weight;Union(V,W,SetType);count++;}}if(count<N-1){printf("-1");}else{printf("%d",fee);}
}
并查集函数
- int Find(int V,int SetType[]):寻找结点V的根节点(采用了路径压缩)
- bool IsSameSet(int V,int W,int SetType[]):判断V,W结点是否在一个集合
- void Union(int V,int W,int SetType[]):将V,W两个结点放在一个集合
int Find(int V,int SetType[])
{if(SetType[V]<0)return V;return SetType[V]=Find(SetType[V],SetType);
}
bool IsSameSet(int V,int W,int SetType[])
{int root1=Find(V,SetType);int root2=Find(W,SetType);if(root1==root2){return true;}else{return false;}
}
void Union(int V,int W,int SetType[])
{int root1=Find(V,SetType);int root2=Find(W,SetType);if(SetType[root2]<SetType[root1]){SetType[root2]+=SetType[root1];SetType[root1]=root2; }else{SetType[root1]+=SetType[root2];SetType[root2]=root1;}
}
最小堆函数
- Heap CreateHeap(int M):建堆
- void BuildMinheap(Heap H);把堆整理为最小堆
- void PercDown(Heap H,int V):从数组下标为V的位置向下过滤(此时V的左子树和右子树都已经是最小堆)
- ElementType DeleteMinHeap(MinHeap H):弹出堆顶元素
#define Infinite 65535
#define VERTEXMAXNUM 3001
typedef struct ENode ElementType;
struct ENode{int V;int W;int Weight;
};
typedef struct HNode* MinHeap;
typedef struct HNode* Heap;
struct HNode{int Size;ElementType HeapMatrix[VERTEXMAXNUM];
};
Heap CreateHeap(int M)
{Heap H=(Heap)malloc(sizeof(struct HNode));H->Size=M;H->HeapMatrix[0].Weight=-1;//哨兵int i=1;for(i=1;i<=M;i++){scanf("%d %d %d",&(H->HeapMatrix[i].V),&(H->HeapMatrix[i].W),&(H->HeapMatrix[i].Weight));}return H;
}
void PercDown(Heap H,int V)
{int T=H->HeapMatrix[V].Weight;ElementType TNode=H->HeapMatrix[V];int parent,child;for(parent=V;parent*2<=H->Size;parent=child){child=2*parent;if(child!=H->Size&&((H->HeapMatrix[child].Weight)>(H->HeapMatrix[child+1].Weight))){child++;}if(H->HeapMatrix[child].Weight<T){H->HeapMatrix[parent]=H->HeapMatrix[child];}else{break;}}H->HeapMatrix[parent]=TNode;
}
void BuildMinheap(Heap H)
{int i=H->Size/2;for(;i>=1;i--){PercDown(H,i);}
}
ElementType DeleteMinHeap(MinHeap H)
{ElementType Node={.Weight=-1};if(H->Size==0){return Node;}Node=H->HeapMatrix[1];H->HeapMatrix[1]=H->HeapMatrix[H->Size--];PercDown(H,1);return Node;
}
公路村村通 (30分)【C语言】Prim算法 和 Kruskal算法相关推荐
- 7-10 公路村村通 (30 分)(C语言)
7-10 公路村村通 (30 分) (写在前面:Kruskal算法不够完善,跪求大佬指点怎么修改,拜托拜托
- 7-5 公路村村通 (30 分)(C语言实现)
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本. 输入格式: 输入数据包括城镇数目正整数N(≤1000)和候选道路数目M(≤3N) ...
- 浙大数据结构:08-图7 公路村村通 (30 分)Prim与Kruskal算法
08-图7 公路村村通 (30 分) 现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本. 输入格式: 输入数据包括城镇数目正整数N( ...
- 5-10 公路村村通 (30分)
5-10 公路村村通 (30分) 现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本. 输入格式: 输入数据包括城镇数目正整数N( ≤ ...
- 7-3 公路村村通 (30分)含解析
7-3 公路村村通 (30分) 现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本. 输入格式: 输入数据包括城镇数目正整数N(≤10 ...
- 08-图7 公路村村通 (30分)
是关于最小生成树的问题(包含v个顶点v-1条边,且边的权重和最小),利用Kruskal贪心算法–将边合并成树,每次都取权值最小的边并且不构成回路,就利用到了并查集的算法(用数组存父节点). 08-图7 ...
- 7-1 公路村村通 (30 分)(C语言版)
先说思路: 看题,使所以点互通,可以看出这使一个求最小生成树的问题. 两种方法: 方法一:Kruskal算法:加边. 方法二:Prim算法:加点. [下面代码有两种,任选其一即可] 现有村落间道路的统 ...
- 08-图7 公路村村通 (30 分)
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本. 输入格式: 输入数据包括城镇数目正整数N(≤)和候选道路数目M(≤):随后的M行 ...
- 7-10 公路村村通 (30 分)
题目描述: 现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本. 输入格式: 输入数据包括城镇数目正整数N(≤1000)和候选道路数目 ...
最新文章
- android Spinner点击事件处理
- 脑洞大开!油画渲染的新算法 Paint Transformer!ICCV2021 Oral!
- Java的位运算符——与()、非(~)、或(|)、异或(^)
- linux如何安装交叉工具链,Linux:制作mipsel 交叉工具链 (更新版)
- ITIL4实用指南 | ITSM的未来属于敏捷
- 信鸽 ios tag推送 php,信鸽推送平台常见问题汇总iOS端
- Lync部署之Lync Mobile服务器端的外网设置
- 高端驱动和低端驱动--ir2110
- linux系统查看dns缓存,如何清空linux的DNS缓存
- Cocos2d-x 3.x 如何编译成安卓程序
- win7笔记本外接显示器html,笔记本连显示器的步骤_笔记本如何外接显示器-win7之家...
- 【Python从入门到精通】二、核心编程:5.正则表达式
- 英特尔的Linux Xe内核图形驱动程序
- 【GitHub】 github如何使用
- FleaPHP 开发指南 - 3. 应用程序入口
- 浏览器兼容性测试怎么做?系统测试工具及方案推荐
- SDIO wifi Marvell8801/Marvell88w8801 介绍(四) ---- Marvell8801/Marvell88w8801命令/事件/数据格式
- 摩摩哒蓄势再起:最新自主研发产品摩舒椅上线小米有品众筹平台
- 【BZOJ4755】【JSOI2016】扭动的回文串
- html如何唤醒app,记一次h5唤醒打开拼多多app