1. 程式人生 > >luoguP1195 口袋的天空

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是將所有邊排序,從最小的邊開始連線兩個點

可能連的兩條最短邊不在同一處,但連下去,總會將所有點連到一起

但是需要注意如果選的邊連出了環,那一定不是最優解,此時連的邊就應該舍掉

這裡我們可以用並查集來判斷是否會連成環

這樣就結束了

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; }