PTA数据结构-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

二、解答

import java.util.*;
//Kruskal:最小堆 + 并查集
public class Main{
    static Road[] minHeap = new Road[3001];
    static int index = 0;
    public static void main(String[] args) {
        //获取数据
        Scanner sc = new Scanner(System.in);
        int cityNum = sc.nextInt();
        int roadNum = sc.nextInt();
        minHeap[0] = new Road(0,0, 0); //设置哨兵
        for (int i = 0; i < roadNum; i++) { inHeap(new Road(sc.nextInt(), sc.nextInt(), sc.nextInt()));}
        sc.close();
        //处理过程  Kruskal   并查集
        int[] coll = new int[cityNum+1];
        int count = 0;
        int lenSum = 0;
        for (int i = 0; i < roadNum; i++) {
            if (count == cityNum - 1) break;
            Road curRoad = outHeap();
            if (collRoad(curRoad, coll)){
                count++;
                lenSum += curRoad.len;
            }
        }
        //输出结果
        int negInt = 0;
        for (int i = 1; i < cityNum+1; i++) { if (coll[i] <= 0) negInt++;}
        if (negInt != 1) lenSum = -1;
        System.out.println(lenSum);
    }
    public static boolean collRoad(Road road, int[] coll){
        if (coll[road.a] == 0 && coll[road.b] == 0) {
            coll[road.a] = -2;
            coll[road.b] = road.a;
            return true;
        }
        if (coll[road.a] == 0) {
            int root = findRoot(road.b, coll);
            coll[road.a] = root;
            coll[root]--;
            return true;
        }
        if (coll[road.b] == 0) {
            int root = findRoot(road.a, coll);
            coll[road.b] = root;
            coll[root]--;
            return true;
        }
        int roota = findRoot(road.a, coll), rootb = findRoot(road.b,coll);
        if (roota == rootb) return false;  //抛弃这条路,否则构成回路
        if (roota < rootb) {  // a集大,b集小
            coll[roota] += coll[rootb];
            coll[rootb] = roota;
        } else {
            coll[rootb] += coll[roota];
            coll[roota] = rootb;
        }
        return true;
    }
    public static int findRoot(int num, int[] coll){
        if (coll[num] == 0) return 0;
        while(coll[num] > 0) num = coll[num];
        return num;
    }
    public static void inHeap(Road road){
        minHeap[++index] = road;
        int temp = index;
        while(minHeap[temp].len < minHeap[temp/2].len){
            Road tempRoad = minHeap[temp/2];
            minHeap[temp/2] = minHeap[temp];
            minHeap[temp] = tempRoad;
            temp /= 2;
        }
    }
    public static Road outHeap(){
        if (index <= 0) return null;
        Road res = minHeap[1];
        minHeap[1] = minHeap[index--];
        int temp = 1;
        while (temp * 2 <= index) {
            if (temp * 2 == index) {
                if (minHeap[temp*2].len < minHeap[temp].len) {
                    Road tempRoad = minHeap[temp * 2];
                    minHeap[temp * 2] = minHeap[temp];
                    minHeap[temp] = tempRoad;
                }
                break;
            } else {
                int min = temp*2+1;
                if (minHeap[temp*2].len < minHeap[temp*2+1].len) min = temp*2;
                if (minHeap[min].len < minHeap[temp].len) {
                    Road tempRoad = minHeap[min];
                    minHeap[min] = minHeap[temp];
                    minHeap[temp] = tempRoad;
                    temp = min;
                } else break;
            }
        }
        return res;
    }
}
class Road{
    int a = 0;
    int b = 0;
    int len = 0;
    public Road(int a, int b, int len){
        this.a = a;
        this.b = b;
        this.len = len;
    }
}

思路:

  1. 什么类型的题?很明显是最小生成树
  2. 如何存放数据?创建Road类
  3. 最小生成树的实现?Prim和Kruskal
  4. 本题实现:RoadNum < 3CityNum 稀疏图   使用Kruskal:最小堆+并查集

思考:

Prim的实现

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值