PTA 08-图7 公路村村通 (30分)
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
输入格式:
输入数据包括城镇数目正整数NN(\le 1000≤1000)和候选道路数目MM(\le 3N≤3N);随后的MM行对应MM条道路,每行给出3个正整数,分别是该条道路直接连通的两个城镇的编号以及该道路改建的预算成本。为简单起见,城镇从1到NN编号。
输出格式:
输出村村通需要的最低成本。如果输入数据不足以保证畅通,则输出-1−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
/* * 这题边数e <= 3*顶点数 不是稀疏图 故以邻接矩阵进行存储 采用prim算法。 (下面也写了个kruskal加最小堆的实现) * findMin()函数找最小值可以用最小堆进行优化。(这里不用最小堆优化用了24ms~~ 用最小堆和kruskal写12ms- -!) */ #include "iostream" using namespace std; #define INF 999999 int map[1005][1005]; int n, m; int lowCost[1005]; long ans = 0; int findMin() { int minCost = INF; int k, j; for (k = 0, j = 1; j <= n; j++) { if (lowCost[j] && lowCost[j] < minCost) { minCost = lowCost[j]; k = j; } } return k; } int prim() { for (int i = 1; i <= n; i++) { lowCost[i] = map[1][i]; } lowCost[1] = 0; /* 从序号为1的顶点出发生成最小生成树 */ for (int i = 1; i < n; i++) { /* 生成树还需要收n-1个节点 */ int k = findMin(); /* 找到到生成树距离最短的节点 */ if (k) { ans += lowCost[k]; lowCost[k] = 0; for (int j = 2; j <= n; j++) { /* 更新当前的最小生成树 */ if (lowCost[j] && map[k][j] < lowCost[j]) { lowCost[j] = map[k][j]; } } } else { return -1; } } return ans; } void init() { cin >> n >> m; for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { if (i == j) { map[i][j] = 0; } else { map[i][j] = INF; } } while (m--) { int c1, c2, c3; cin >> c1 >> c2 >> c3; map[c1][c2] = map[c2][c1] = c3; } } int main() { init(); cout<<prim()<<endl; }
/* * 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; }
此鸟不飞则已,一飞冲天,不鸣则已,一鸣惊人~~~ 我要做这只鸟儿~~ O(∩_∩)O哈!