luoguP1195 口袋的天空

emmmmm

最小生成树,kruskal

大概是dtx教的,%%%

这道题是学长推荐做的(我点名要水题

/*超喜欢题面嗷

漂亮的天空上有漂亮的云彩,漂亮的孩子做着漂亮的梦

(而且立刻联想到小云彩嗷嗷嗷   qwq*/

无视以上废话,看题

其实和最小生成树的模版题没什么区别(悄咪咪表示某谷测试点里没有输出orz的点

我们只需要想一下要做出k个棉花糖

就是生成k棵树

总共n个点

生成1棵树连边n - 1条

生成2棵树连边n - 2条

.

.

.

生成k棵树连边n - k条

在kruskal函数中加一个计数就好了

然后我在这里再讲一下kruskal吧

(prim没人给我讲,也看不懂【斜眼笑

众所周知(???我可能看学长博客多了?用词突然相似

kruskal和prim是求最小生成树的两种算法

区别嘛。。。应该是,kruskal将边排序,prim更新节点连最小边?

都是贪心的思路

从任意点出发,首先选择最短的边与之相连,再选择与这棵树之间距离最短的边相连,最后输出的一定是最优解(这是两者的总体思路

那么kruskal是将所有边排序,从最小的边开始连接两个点

可能连的两条最短边不在同一处,但连下去,总会将所有点连到一起

但是需要注意如果选的边连出了环,那一定不是最优解,此时连的边就应该舍掉

(orz被学长指出其实是因为不符合树的性质orz【捂脸

这里我们可以用并查集来判断是否会连成环

这样就结束了

emmmm看代码吧

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 10010;
int n,m,k,flag = 0;
int fa[maxn];
struct edge{
  int from,to,weight;
}a[maxn];//用结构体记录每条边连接的点和边权
int get(int x){
  if(fa[x] == x)
    return x;
  else return fa[x] = get(fa[x]);
}//并查集
int cmp(edge x,edge y){
  return x.weight < y.weight;
}//排序的定义(根据边权大小
int kruskal(){
  int ans = 0,num = 0;
  sort(a,a + m,cmp);
  for(int i = 0;i < m;i++){
    int l = get(a[i].from);
    int r = get(a[i].to);
    if(l != r){//判断是否成环
      fa[l] = r;
      ans += a[i].weight;
      num++;
      if(num == n - k){//计数,判断是否能生成k棵树
    flag = 1;//标记一下
    break;
      }
    }
  }
  return ans;
}
int main(){
  scanf("%d%d%d",&n,&m,&k);
  for(int i = 0;i < n;i++)
    fa[i] = i;
  for(int i = 0;i < m;i++)
    scanf("%d%d%d",&a[i].from,&a[i].to,&a[i].weight);
  if(flag == 0)
    printf("%d",kruskal());
  else printf("No Answer");
  return 0;
}
原文地址:https://www.cnblogs.com/sevenyuanluo/p/10010039.html