08-图7 公路村村通 (30 分)
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
输入格式:
输入数据包括城镇数目正整数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
Note1
- 思路是库鲁斯卡尔 + 并查集(按质归并+路径压缩)
- 利用visited数组筛选最小边,暴力导致两个测试点超时
- 老毛病,递归的find函数当 s[x] >= 0时,没有返回值,导致只有样例程序能跑的通(结果便是只有根的孩子是-1,子孙都变成编译器默认的返回值。)
- 还需优化: 用堆筛选最小边
Code1
#include<iostream>
using namespace std;
#define MAX 1000
int a[MAX][MAX];
int s[MAX];
int Find(int x){
if(s[x] < 0){
return x;
}
else return s[x] = Find(s[x]);
}
void Union( int v1, int v2){
if(s[v2] < s[v1]){
s[v1] = v2;
}
else{
if(s[v1] == s[v2]) s[v1]--;
s[v2] =v1;
}
}
int main() {
int sum = 0, num, arcnum, min;
int p1, p2;
int i, j;
int visited[MAX][MAX];
cin >> num >> arcnum;
int nums = num;
for(i = 0; i < num; i++) {
for( j = 0; j < num; j++){
a[i][j] = MAX;
visited[i][j] = 0;
}
s[i] = -1;
}
for(i = 0; i < arcnum; i++){
int tempa , tempb, tempc;
cin >> tempa >> tempb >> tempc;
a[tempa - 1][tempb - 1] = tempc;
a[tempb - 1][tempa - 1] = tempc;
}
while(nums > 1){
min = MAX;
int temp1, temp2;
for( i = 0; i < num; i++) {
for( j = 0; j < num; j++) {
if(a[i][j] < min && visited[i][j] == 0){
min = a[i][j];
temp1 = i;
temp2 = j;
}
}
}
if(min < MAX){
visited[temp1][temp2] = 1;
visited[temp2][temp1] = 1;
}
if(min == MAX){
break;
}
p1 = Find(temp1);
p2 = Find(temp2);
if(p1 != p2){
Union(p1, p2);
nums--;
sum += min;
}
}
if (nums > 1) printf("-1");
else printf("%d", sum);
return 0;
}
Note2
- 手写堆的插入和删除,其实stl里面的优先队列可以实现。。
- 发现之前的邻接矩阵无法保存图中边和两节点的关系,更换数据结构
- MAX在vs2012取1000时会出错,导致以为自己的delete函数写错,查了好久
Code2
#include<iostream>
using namespace std;
#define MAX 1000
int s[MAX];
int size = 0;
struct arcs{
int weight;
int x;
int y;
}arc[3 * MAX], heap[3*MAX];
void Insert(arcs n){
int i;
for( i = ++size; n.weight < heap[i / 2].weight; i /= 2){
heap[i] = heap[i/2];
}
heap[i] = n;
}
arcs Delete(void){
arcs back = heap[1];
arcs last = heap[size--];
int parent = 1;
while(2 * parent <= size){
int child = 2 * parent ;
if (heap[2 * parent + 1].weight < heap[2 * parent ].weight)/*若右节点的元素值更小,则调整child*/
child++;
if (last.weight <heap[child].weight)
break;
else
heap[parent ] =heap[child];
parent = child;
}
heap[parent] = last;
return back;
}
int Find(int x){
if(s[x] < 0){
return x;
}
else return s[x] = Find(s[x]);
}
void Union( int v1, int v2){
if(s[v2] < s[v1]){
s[v1] = v2;
}
else{
if(s[v1] == s[v2]) s[v1]--;
s[v2] =v1;
}
}
int main() {
int sum = 0, num, arcnum;
int p1, p2;
int i, j;
int visited[MAX][MAX];
cin >> num >> arcnum;
int nums = num;
for(i = 0; i < num; i++) {
s[i] = -1;
heap[i].weight = MAX;
}
heap[0].weight = -1;
for(i = 0; i < arcnum; i++){
cin >> arc[i].x >> arc[i].y >> arc[i].weight;
Insert(arc[i]);
}
while(nums > 1){
arcs min = Delete();
if(size < 1){
break;
}
p1 = Find(min.x);
p2 = Find(min.y);
if(p1 != p2){
Union(p1, p2);
nums--;
sum += min.weight;
}
}
if (nums > 1) printf("-1");
else printf("%d", sum);
return 0;
}