题目背景 小杉坐在教室里,透过口袋一样的窗户看口袋一样的天空。 有很多云飘在那里,看起来很漂亮,小杉想摘下那样美的几朵云,做成棉花糖。
给你云朵的个数N,再给你M个关系,表示哪些云朵可以连在一起。
现在小杉要把所有云朵连成K个棉花糖,一个棉花糖最少要用掉一朵云,小杉想知道他怎么连,花费的代价最小。
本题通过代价最小可以锁定是最小生成树的题目,首选并查集,kurscal算法。
本题关键在于审题,实现并没有难度,一开始老是挂,一开始的思路是,题目种说一个棉花糖最少要用掉一朵云,那么我每次连上一条边就能有2个棉花糖,直接用并查集加边,但是一直是错的,只过了一个点,我反复检查了代码并没有发现错误,因此,只可能是题目看错了。我在想为什么要给n个点,只要有k个点就可以了,后来转念一想,他说的k个棉花糖的意思是要用n个点组成k个生成树。审题非常关键,有时候代码并不难写,反而是题目都读不清楚,造成错误。
k个生成树,可以用推断法:kruscal算法生成一棵最小生成树,需要n-1条边,那么2棵树就需要n-2条边,k棵树就要n-k条边,只要我们能找到n-k条边就完成了题目要求。
public class Main {
static List<edge> edges = new ArrayList<edge>();
static int m,n,k;
static int[] id;
public static void main(String[] args) throws Exception{
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String[] sa = reader.readLine().split(" ");
n=Integer.parseInt(sa[0]);
m=Integer.parseInt(sa[1]);
k=Integer.parseInt(sa[2]);
for(int i=0;i<m;i++){
String[] sb = reader.readLine().split(" ");
edges.add(new edge(Integer.parseInt(sb[0]),Integer.parseInt(sb[1]),Integer.parseInt(sb[2])));
}
Collections.sort(edges);
init();
int res =0;
int cost=0;
for(edge edge:edges){
if(find(edge.u)!=find(edge.v)){
cost++;
union(edge.u,edge.v);
res+=edge.w;
if(cost==n-k) break;
}
}
if(cost<n-k) System.out.println("No Answer");
else System.out.println(res);
}
//接下来三个函数实现并查集
public static void init(){
id = new int[n+1];
for(int i=1;i<=n;i++) id[i] = i;
}
public static int find(int x){
while(x!=id[x]) x = id[id[x]];
return x;
}
public static void union(int x,int y){
x = find(x);
y = find(y);
if(x==y) return;
id[y] = x;
}
//保存边的信息
static class edge implements Comparable<edge> {
int u,v,w;
public edge(int u,int v,int w){
this.u = u;
this.v = v;
this.w = w;
}
@Override
public int compareTo(edge o) {
if(this.w > o.w) return 1;
if(this.w == o.w) return 0;
else return -1;
}
}
}