luoguP1195 口袋的天空
阿新 • • 發佈:2018-11-24
emmmmm
最小生成樹,kruskal
大概是dtx教的,%%%(他部落格沒申請好,以後再放連結來膜
這道題是學長推薦做的(我點名要水題
/*超喜歡題面嗷
漂亮的天空上有漂亮的雲彩,漂亮的孩子做著漂亮的夢
無視以上廢話,看題
其實和最小生成樹的模版題沒什麼區別(悄咪咪表示某谷測試點裡沒有輸出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; }