關於kruskal與prim演算法在題目中的運用
一般來說,能用prim做的題目,一般用kruskal也能做,而且複雜度更低,而kruskal能做的題prim有可能解決不了,但是在我今天做的題目發現其實在某些時刻還是應該選用prim而不是kruskal,題目如下
P1991 無線通訊網
國防部計劃用無線網路連線若干個邊防哨所。2 種不同的通訊技術用來搭建無線網路;
每個邊防哨所都要配備無線電收發器;有一些哨所還可以增配衛星電話。
任意兩個配備了一條衛星電話線路的哨所(兩邊都ᤕ有衛星電話)均可以通話,無論他們相距多遠。而只通過無線電收發器通話的哨所之間的距離不能超過 D,這是受收發器的功率限制。收發器的功率越高,通話距離 D 會更遠,但同時價格也會更貴。
收發器需要統一購買和安裝,所以全部哨所只能選擇安裝一種型號的收發器。換句話說,每一對哨所之間的通話距離都是同一個 D。你的任務是確定收發器必須的最小通話距離 D,使得每一對哨所之間至少有一條通話路徑(直接的或者間接的)。
輸入輸出格式
輸入格式:
從 wireless.in 中輸入資料第 1 行,2 個整數 S 和 P,S 表示可安裝的衛星電話的哨所數,P 表示邊防哨所的數量。接下里 P 行,每行兩個整數 x,y 描述一個哨所的平面座標(x, y),以 km 為單位。
輸出格式:
輸出 wireless.out 中
第 1 行,1 個實數 D,表示無線電收發器的最小傳輸距離,精確到小數點後兩位。
輸入輸出樣例
輸入樣例#1: 複製
2 4 0 100 0 300 0 600 150 750
輸出樣例#1: 複製
212.13
說明
對於 20% 的資料:P = 2,S = 1
對於另外 20% 的資料:P = 4,S = 2
對於 100% 的資料保證:1 ≤ S ≤ 100,S < P ≤ 500,0 ≤ x,y ≤ 10000。
// luogu-judger-enable-o2 #include<iostream> #include<cmath> #include<vector> #include<algorithm> using namespace std; vector<double> v; struct Node{ int x, y; int cnt; }node[510]; struct edge{ int u, v; double cost; }E[250010]; bool cmp(edge a, edge b) { return a.cost < b.cost; } int father[510]; int findFather(int x) { while (x != father[x]) { x = father[x]; } return x; } int kruskal(int n, int m) { int Num_Edge = 0; for (int i = 0; i < n; i++) { father[i] = i; } sort(E, E + m, cmp); for (int i = 0; i < m; i++) { int faU = findFather(E[i].u); int faV = findFather(E[i].v); if (faU != faV) { father[faU] = faV; v.push_back(E[i].cost); Num_Edge++; if (Num_Edge == n - 1) break; } } if (Num_Edge != n - 1) return -1; else return 1; } int main() { int s, p; cin >> s >> p; for (int i = 0; i < p; i++) { cin >> node[i].x >> node[i].y; node[i].cnt = i; } int cnt = 0; for (int i = 0; i < p; i++) { for (int j = i + 1; j < p; j++) { double dis = sqrt(pow(node[i].x - node[j].x, 2) + pow(node[i].y - node[j].y, 2)); E[cnt].u = node[i].cnt; E[cnt].v = node[j].cnt; E[cnt++].cost = dis; } } kruskal(p, cnt); printf("%.2f", v[v.size() - s]); return 0; }
思考:關於這道題目其實爭議挺多的,有人說要刪除邊啥的,有人是用瓶頸生成樹來做的,不過我感覺我的這種思路算是比較大眾的思路,就是一個kruskal一套下來,然後貪心從後往前取就可以了,在這裡我使用了結構體來處理座標的問題,直接把他轉換成了一個結點,然後按照常規的套路來便可以了,然而接下來看下一道題目
P1265 公路修建
某國有n個城市,它們互相之間沒有公路相通,因此交通十分不便。為解決這一“行路難”的問題,政府決定修建公路。修建公路的任務由各城市共同完成。
修建工程分若干輪完成。在每一輪中,每個城市選擇一個與它最近的城市,申請修建通往該城市的公路。政府負責審批這些申請以決定是否同意修建。
政府審批的規則如下:
(1)如果兩個或以上城市申請修建同一條公路,則讓它們共同修建;
(2)如果三個或以上的城市申請修建的公路成環。如下圖,A申請修建公路AB,B申請修建公路BC,C申請修建公路CA。則政府將否決其中最短的一條公路的修建申請;
(3)其他情況的申請一律同意。
一輪修建結束後,可能會有若干城市可以通過公路直接或間接相連。這些可以互相:連通的城市即組成“城市聯盟”。在下一輪修建中,每個“城市聯盟”將被看作一個城市,發揮一個城市的作用。
當所有城市被組合成一個“城市聯盟”時,修建工程也就完成了。
你的任務是根據城市的分佈和前面講到的規則,計算出將要修建的公路總長度。
輸入輸出格式
輸入格式:
第一行一個整數n,表示城市的數量。(n≤5000)
以下n行,每行兩個整數x和y,表示一個城市的座標。(-1000000≤x,y≤1000000)
輸出格式:
一個實數,四捨五入保留兩位小數,表示公路總長。(保證有惟一解)
輸入輸出樣例
輸入樣例#1: 複製
4 0 0 1 2 -1 2 0 4
輸出樣例#1: 複製
6.47
說明
修建的公路如圖所示:
思考:看到這個題目是不是感覺特別熟悉有木有,感覺就是和上面那個題目一模一樣,就是換了個法子問你,然後我就把上面的程式碼稍微做了修改,然後把樣例輸了一遍,過了,然後我信心滿滿的交了上去,有倆個測試點記憶體不夠,然後我就開始吧int改成long long各種改,大概過了30分鐘還是沒有頭緒,後來看了別人的部落格才知道如果矩陣開到5000*5000是會記憶體超限的,我竟然糊塗到把這個都忘了,那麼問題來了,接下來要怎麼修改,這個時候我們仔細看一下題目就會發現,這是一個稠密圖,也就是點少邊多,完全適用於prim演算法,而且還可以直接用矩陣來儲存點的值,接下來直接上程式碼
// luogu-judger-enable-o2
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXV = 5010;
int p[MAXV][2];
const int INF = 1e9;
double d[MAXV];
bool vis[MAXV];
double dist(int x1,int y1,int x2,int y2)
{
return sqrt((double)(x1-x2)*(x1-x2)+(double)(y1-y2)*(y1-y2));
}
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> p[i][0] >> p[i][1];
}
double ans = 0;
fill(d, d + MAXV, INF);
d[0] = 0;
for (int i = 0; i < n; i++) {
int u = -1;
double Min = INF;
for (int j = 0; j < n; j++) {
if (d[j] < Min && vis[j] == false) {
Min = d[j];
u = j;
}
}
vis[u] = true;
ans += d[u];
for (int v = 0; v < n; v++) {
double t=dist(p[u][0],p[u][1],p[v][0],p[v][1]);
if (t < d[v]){
d[v] = t;
}
}
}
printf("%.2f", ans);
return 0;
}
那麼問題又來了,既然這個題目可以用prim,那麼上一個題應該也是可以用prim來做的,事實上就是如此,這個題目真是一個比較好的教訓,以後還是要根據實際情況來使用相對應的演算法