一、题目
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
输入格式:
输入数据包括城镇数目正整数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;
}
}
思路:
- 什么类型的题?很明显是最小生成树
- 如何存放数据?创建Road类
- 最小生成树的实现?Prim和Kruskal
- 本题实现:RoadNum < 3CityNum 稀疏图 使用Kruskal:最小堆+并查集
思考:
Prim的实现