利用Kruskal演算法求最小生成樹解決聰明的猴子問題 -- 資料結構
阿新 • • 發佈:2019-05-19
題目:聰明的猴子
連結:https://ac.nowcoder.com/acm/problem/19964
輸入描述:
第1行為一個整數,表示猴子的個數M(2 ≤ M ≤ 500);
第2行為M個整數,依次表示猴子的最大跳躍距離(每個整數值在1--1000之間);
第3行為一個整數表示樹的總棵數N(2 ≤ N ≤ 1000);
第4行至第N+3行為N棵樹的座標(橫縱座標均為整數,範圍為:-1000--1000)。(同一行的整數間用空格分開)
輸出描述:
包括一個整數,表示可以在這個地區的所有樹冠上覓食的猴子數示例1
輸入
複製4 1 2 3 4 6 0 0 1 0 1 2 -1 -1 -2 0 2 2
輸出
複製3
分析:
1.題目中的樹冠上覓食的猴子數是指能夠在所有樹上自由移動的猴子數;
2.為了解決這一道題,我們應該要求出最小生成樹中最長邊的大小,再用每個猴子能夠移動的最大距離逐一比較;
3.這裡我將採用Kruskal演算法求最小生成樹:
①將所有邊按權值排序(以升序為例)
②按照邊的排序構建最小生成樹
程式碼:
1.頂點結構定義:
typedef struct point{ int x, y;//座標(x,y) bool status; }point;
2.邊結構定義:
typedef struct edge{ point p1, p2;//邊的兩個端點 double weight;//邊的權值 }edge;
3.對edge e[]進行升序排序:
//按權值快速排序 qsort(e, tree_num*(tree_num-1)/2, sizeof(edge), cmp);
//cmp函式 int cmp(const void *a, const void *b) { return (*(edge *)a).weight > (*(edge *)b).weight ? 1 : -1; }
4.Kruskal函式
1 /*傳入: 2 edge *e 存放邊的陣列 3 int tree_num 樹的數量 4 point *p 存放點的陣列 5 */ 6 7 double kruskal(edge *e, int tree_num, point *p) 8 { 9 //並查集 10 int v[tree_num]; 11 for(int i=0; i<tree_num; i++){ 12 v[i] = i; 13 } 14 15 double longest; 16 17 int i1, i2; 18 for(int i=0; i<tree_num*(tree_num-1)/2; i++){ 19 if(search(p, e[i].p1, tree_num) == -1 || search(p, e[i].p2, tree_num) == -1){ 20 exit(-1);//ERROR:沒有找到e[i].p1在p[]中的座標 21 } 22 //serach()查詢 e[i].p1在p[]中的座標 23 i1 = v[search(p, e[i].p1, tree_num)]; 24 i2 = v[search(p, e[i].p2, tree_num)]; 25 26 27 if(i1 == i2 == tree_num-1)break;//已經構建成最小生成樹 28 if(i1 == i2)continue;//邊的兩個端點已經在同一個集合中 29 30 //將i1,i2中較大的作為標記 31 int i3 = i1>i2 ? i1:i2; 32 for(int j=0; j<tree_num; j++){ 33 if(v[j] == i1 || v[j] == i2){ 34 v[j] = i3; 35 } 36 } 37 longest = e[i].weight;//更新長邊 38 } 39 return longest;//返回最長邊 40 }
5.search函式(Kruskal函式中呼叫):
int search(point *p, point p1, int tree_num) { for(int i=0; i<tree_num; i++){ if(p1.x == p[i].x && p1.y == p[i].y){ return i; } } return -1; }
6.所有程式碼:
#include<iostream> #include<cmath> #include<cstring> #include <stdlib.h> using namespace std; #define max 1000 typedef struct point{ int x, y;//座標(x,y) bool status; }point; typedef struct edge{ point p1, p2;//邊的兩個端點 double weight;//邊的權值 }edge; int cmp( const void *a ,const void *b); double kruskal(edge *e, int tree_num, point *p); int get_root(int *v, int x); int search(point *p, point p1, int tree_num); int main(){ //輸入猴子數量 int monkey_num; cin>>monkey_num; //輸入猴子能到達的最遠距離 int step[monkey_num]; memset(step, 0, sizeof(step)); for(int i=0; i<monkey_num; i++){ cin>>step[i]; } //輸入樹的數量 int tree_num; cin>>tree_num; //輸入樹的座標(點記錄) point p[max]; memset(p, 0, sizeof(p)); for(int i=0; i<tree_num; i++){ cin>>p[i].x>>p[i].y; p[i].status = 1;//status為1表示可操作的樹 } //邊記錄 edge e[max]; int t = 0; int vi[max] = {0}; for(int i=0; p[i].status != 0; i++){ for(int j=0; p[j].status != 0 ; j++){ if(i != j && vi[j] == 0){//增加邊的權值,兩個端點 e[t].weight = sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x) + (p[i].y-p[j].y)*(p[i].y-p[j].y)); e[t].p1 = p[i]; e[t++].p2 = p[j]; } } vi[i] = 1;//標記p[i]點連線的所有邊已經被讀取 } //按權值快速排序 qsort(e, tree_num*(tree_num-1)/2, sizeof(edge), cmp); double longest = kruskal(e, tree_num, p); int ans = 0; for(int i=0; i<monkey_num; i++){ if(step[i] >= longest)ans++; } cout<<ans; return 0; } int cmp(const void *a, const void *b) { return (*(edge *)a).weight > (*(edge *)b).weight ? 1 : -1; } double kruskal(edge *e, int tree_num, point *p) { //並查集 int v[tree_num]; for(int i=0; i<tree_num; i++){ v[i] = i; } double longest; int i1, i2; for(int i=0; i<tree_num*(tree_num-1)/2; i++){ if(search(p, e[i].p1, tree_num) == -1 || search(p, e[i].p2, tree_num) == -1){ exit(-1);//ERROR:沒有找到e[i].p1在p[]中的座標 } //serach()查詢 e[i].p1在p[]中的座標 i1 = v[search(p, e[i].p1, tree_num)]; i2 = v[search(p, e[i].p2, tree_num)]; if(i1 == i2 == tree_num-1)break;//已經構建成最小生成樹 if(i1 == i2)continue;//邊的兩個端點已經在同一個集合中 //將i1,i2中較大的作為標記 int i3 = i1>i2 ? i1:i2; for(int j=0; j<tree_num; j++){ if(v[j] == i1 || v[j] == i2){ v[j] = i3; } } longest = e[i].weight; } return longest; } int search(point *p, point p1, int tree_num) { for(int i=0; i<tree_num; i++){ if(p1.x == p[i].x && p1.y == p[i].y){ return i; } } return -1; }ALL
總結:
很多時候還是聽懂容易實踐難,難就難在為了實現功能要有一層一層縝密的邏輯需要構建,漏了一種情況都會影響結果的。
(外面的oj真的很嚴格呢)
參考資料:
【演算法】圖的最小生成樹(Kruskal演算法)