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
AC代码
prim算法
/*!
* \file 08-图7 公路村村通.cpp
*
* \author ranjiewen
* \date 2017/05/01 10:44
*
*
*/
#include <iostream>
using namespace std;
#define INF 65535
#define MaxVertexNum 1001
int map[MaxVertexNum][MaxVertexNum]; /*邻接矩阵表示*/
int Nv, Ne; //顶点,边数
int cost[MaxVertexNum]; //即dist[],没有包含在生成树外的节点到MST的最短距离
long ret_cost = 0;
//这题边数e <= 3*顶点数;不是稀疏图;故以邻接矩阵进行存储 采用prim算法。
int findMin()
{
int minCost = INF;
int k=0, j;
for ( j = 1; j <= Nv;j++)
{
if (cost[j]!=0&&cost[j]<minCost)
{
minCost = cost[j];
k = j;
}
}
return k;
}
int prim()
{
for (int i = 1; i <= Nv;i++)
{
cost[i] = map[1][i]; //直接和第一个节点相连的节点;这样后面找最小值才能找到
}
cost[1] = 0; //从序号为1的顶点出发生成最小生成树,置1表示已经在集合内了,即访问过
for (int i = 1; i < Nv;i++) /* 生成树还需要收n-1个节点 */
{
int k = findMin();/* 找到到生成树距离最短的节点 */
if (k!=0) //找到了
{
ret_cost += cost[k];
cost[k] = 0; //收到集合内
for (int j = 2; j <= Nv;j++) /* 更新当前的最小生成树 */
{
if (cost[j]&&map[k][j]<cost[j])
{
cost[j] = map[k][j];
}
}
}
else
{
return -1;
}
}
return ret_cost;
}
void init()
{
cin >> Nv >> Ne;
for (int i = 1; i <= Nv; i++)
for (int j = 1; j <= Nv; j++)
{
if (i == j)
{
map[i][j] = 0;
}
else {
map[i][j] = INF;
}
}
while (Ne--)
{
int V, W, Weight;
cin >> V >> W >> Weight;
map[V][W] = map[W][V] = Weight;
}
}
int main()
{
init();
cout << prim() << endl; //判断是否非连通,将收集的顶点计数, if ( VCount < Graph->Nv ) /* MST中收的顶点不到|V|个 */ return = ERROR;
return 0;
}
kruskal加最小堆的实现
/*
* kruskal加最小堆的实现(可能是数据量比较小吧~ 这个稠密图用krukal加了个最小堆跑的比前面的prim快了一倍)
*/
#include "iostream"
using namespace std;
#define INF 0
struct Node {
int s, e;
int cost;
}edge[3001];
int father[1001];
int Size = 0;
int v, e;
void makeHeap() { /* 将数组调整成小顶堆 O(e)的时间复杂度 */
int child, parent;
Size = e;
edge[0].cost = INF; /* 哨兵 */
int i = Size / 2;
for (; i >= 1; i--) {
Node temp = edge[i];
for (parent = i; parent * 2 <= Size; parent = child)
{
child = parent * 2; /* 先指向左孩子 */
if ((child != Size) && edge[child].cost > edge[child + 1].cost) { /* 右孩子*/
child++;
}
if (temp.cost <= edge[child].cost) {
break;
}
else
edge[parent] = edge[child];
}
edge[parent] = temp;
}
}
Node deleteMinFromHeap() { /* 从小顶堆中删除元素 在调整成小顶堆 时间复杂度O(log e)*/
Node temp = edge[Size--];
Node minItem = edge[1];
//cout << Size << endl;
int parent, child;
for ( parent = 1; parent * 2 <= Size; parent = child) {
child = parent * 2;
if ((child != Size) && (edge[child].cost > edge[child+1].cost)) {
child += 1; /* 找左右孩子中较小者 */
}
if (temp.cost <= edge[child].cost) break;
else edge[parent] = edge[child];
}
edge[parent] = temp;
return minItem;
}
void init() {
// cout << "v--->" << v << endl;
for (int i = 1; i <= v; i++)
father[i] = -1; /* 初始化为当前树的节点数的相反数 */
}
int find(int x) { /* 查询根节点 */
if (father[x] <= -1)
return x;
else
return father[x] = find(father[x]); /* 路径压缩 */
}
void Union(int x, int y) {
x = find(x);
y = find(y);
if (father[x] < father[y]) { /* 按节点数大小进行归并 */
father[x] += father[y];
father[y] = x;
}
else {
father[y] += father[x];
father[x] = y;
}
}
int main() {
int ans = 0;
cin >> v >> e;
for (int i = 1; i <= e; i++) {
cin >> edge[i].s >> edge[i].e >> edge[i].cost;
}
makeHeap();
init();
int k = 1;
for(int i = 1 ;i <= e; i++) {
Node node = deleteMinFromHeap(); /* 总的复杂度 O(e * log v) */
int x = find(node.s); /*O(1)*/
int y = find(node.e);
if (x != y) {
ans += node.cost;
Union(node.s, node.e); /*O(1)*/
k++;
if (k == v) {
break;
}
}
}
if (k != v)
cout << -1 << endl;
else
cout << ans << endl;
return 0;
}
Reference
08-图7 公路村村通 写的不错
最小生成树之prim算法 原理一样
C/C++基本语法学习
STL
C++ primer