2020藍橋杯校內賽-一帶一路-圖論-最小生成樹

題面

問題描述
  2015年,全中國實現了戶戶通電。做爲一名電力建設者,小明正在幫助一帶一路上的國家通電。
  這一次,小明要幫助 n 個村莊通電,其中 1 號村莊正好能夠創建一個發電站,所發的電足夠全部村莊使用。
  如今,這 n 個村莊之間都沒有電線相連,小明主要要作的是架設電線鏈接這些村莊,使得全部村莊都直接或間接的與發電站相通。
  小明測量了全部村莊的位置(座標)和高度,若是要鏈接兩個村莊,小明須要花費兩個村莊之間的座標距離加上高度差的平方,形式化描述爲座標爲 (x_1, y_1) 高度爲 h_1 的村莊與座標爲 (x_2, y_2) 高度爲 h_2 的村莊之間鏈接的費用爲
  sqrt((x_1-x_2)*(x_1-x_2)+(y_1-y_2)*(y_1-y_2))+(h_1-h_2)*(h_1-h_2)。
  在上式中 sqrt 表示取括號內的平方根。請注意括號的位置,高度的計算方式與橫縱座標的計算方式不一樣。
  因爲經費有限,請幫助小明計算他至少要花費多少費用才能使這 n 個村莊都通電。
輸入格式
  輸入的第一行包含一個整數 n ,表示村莊的數量。
  接下來 n 行,每一個三個整數 x, y, h,分別表示一個村莊的橫、縱座標和高度,其中第一個村莊能夠創建發電站。
輸出格式
  輸出一行,包含一個實數,四捨五入保留 2 位小數,表示答案。
樣例輸入
4
1 1 3
9 9 7
8 8 6
4 5 4
樣例輸出
17.41
評測用例規模與約定
  對於 30% 的評測用例,1 <= n <= 10;
  對於 60% 的評測用例,1 <= n <= 100;
  對於全部評測用例,1 <= n <= 1000,0 <= x, y, h <= 10000。

題解

經過閱讀題面瞭解到是最小生成樹的模板題。接收完數據後,須要經過題目給出的公式算出兩點之間的權重,結果保存到一個鄰接矩陣裏(稠密圖)。最後使用普里姆算法,設置第一個結點爲起點,求出最小生成樹。最後將全部邊的權值加起來就是答案。輸出能夠使用printf("%.2f")格式化輸出,最自動四捨五入。ios

代碼

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#define N 1001
#define WHITE 0
#define GRAY 1
#define BLACK 2
#define INFTY 1 << 21

using namespace std;

double M[N][N], d[N];
int color[N], p[N], n;
struct Node {
  double x, y, h;
} arr[N];

void prim () {
  for (int i = 0; i < N; i++) {
    color[i] = WHITE;
    d[i] = INFTY;
  }

  d[0] = 0;
  p[0] = -1;

  int mincost, u;
  
  while (1) {
    mincost = INFTY;
    
    for (int i = 0; i < n; i++) {
      if (color[i] != BLACK && d[i] < mincost) {
        mincost = d[i];
        u = i;
      }
    }

    if (mincost == INFTY)
      break;

    color[u] = BLACK;

    for (int v = 0; v < n; v++) {
      if (color[v] != BLACK && M[u][v]) {
        d[v] = M[u][v];
        p[v] = u;
        color[v] = GRAY;
      }
    }
  }
}

int main () {
  cin >> n;

  double x, y, h;
  
  for (int i = 0; i < n; i++) {
    cin >> x >> y >> h;
    arr[i].x = x;
    arr[i].y = y;
    arr[i].h = h;
  }

  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      if (i == j)
        M[i][j] = 0;
      else {
        x = (arr[i].x - arr[j].x) * (arr[i].x - arr[j].x);
        y = (arr[i].y - arr[j].y) * (arr[i].y - arr[j].y);
        h = (arr[i].h - arr[j].h) * (arr[i].h - arr[j].h);
        M[i][j] = sqrt(x + y) + h;
      }
    }
  }

  prim();

  double ans = 0;
  for (int i = 0; i < n; i++)
    ans += d[i];

  printf("%.2f", ans);

  return 0;
}

知識點

  • 圖論;
  • 最小生成樹;
  • 普里姆算法。
相關文章
相關標籤/搜索