08-图7 公路村村通 (30 分)

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

  1. 思路是库鲁斯卡尔 + 并查集(按质归并+路径压缩)
  2. 利用visited数组筛选最小边,暴力导致两个测试点超时
  3. 老毛病,递归的find函数当 s[x] >= 0时,没有返回值,导致只有样例程序能跑的通(结果便是只有根的孩子是-1,子孙都变成编译器默认的返回值。)
  4. 还需优化: 用堆筛选最小边

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

  1. 手写堆的插入和删除,其实stl里面的优先队列可以实现。。
  2. 发现之前的邻接矩阵无法保存图中边和两节点的关系,更换数据结构
  3. 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值