次小產生樹的應用
總思路是枚舉每條邊作為那條免費的路;
當你確定一條免費的路之後,相當於在這兩個點之間加了一條長度為0的邊之後求MST;
根據劉汝佳所說,MST就是把所有環中的最大邊刪除,我們首先要求出MST
如果枚舉的邊屬於MST,則他原來不是環上的最大邊,現在也不是,該這條邊為0後MST的結構不變,只是MST的總長度少了這條邊的長度;
如果枚舉的邊不屬於MST,則他原來肯定是某個環上的最大邊,這條邊長度為0之後刪除的“最大邊”就是原來環上的第二大的邊,也就是原MST結構中這兩個點之間的最大邊;
在求出MST後我們可以預先處理求出所有(i, j)之間的最大邊
所求的MST需要這些內容
//1.某邊是否屬於MST
//2.MST上沒兩個點間的最大邊
//3.MST的鄰接表,用於求所有(i, j)之間的最大邊
#include <stdio.h>#include <string.h>#include <math.h>#include <algorithm>#include <vector>#include <queue>using namespace std;const int maxn = 1000+10;struct Node{ int from, to; double dist; Node(int from, int to, double dist) { this->from = from; this->to = to; this->dist = dist; } bool operator<(const struct Node &ans)const { return dist > ans.dist; }};double popu[maxn], edge[maxn][maxn], sum;int x[maxn], y[maxn];bool vis[maxn], flag[maxn][maxn];//是否屬於樹double dist[maxn];vector<int> adj[maxn];//MST的鄰接表double EdgeM[maxn][maxn];//每兩個點間的最大邊int tcase, n;void Init(){ scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d%d%lf", &x[i], &y[i], &popu[i]); for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) { double xx = x[i] - x[j]; double yy = y[i] - y[j]; edge[i][j] = sqrt(xx*xx+yy*yy); }}void Prim(){ priority_queue<struct Node> myQue; for(int i = 1; i <= n; i++) { dist[i] = -1; vis[i] = false; adj[i].clear(); memset(flag[i], false, sizeof(flag[i])); } sum = 0;//MST的總長度 dist[1] = 0; vis[1] = true; for(int i = 2; i <= n; i++)//以1為起始集合 { dist[i] = edge[1][i]; myQue.push(Node(1, i, dist[i])); } while(!myQue.empty()) { struct Node ans = myQue.top(); myQue.pop(); int f = ans.from; int u = ans.to; if(vis[u]) continue; sum += edge[f][u]; vis[u] = true;//改點已經加入MST了 flag[f][u] = flag[u][f] = true; adj[u].push_back(f); adj[f].push_back(u); for(int v = 1; v <= n; v++) if((v != u && !vis[v]) && (dist[v] == -1 || dist[v] > edge[u][v])) { dist[v] = edge[u][v]; myQue.push(Node(u, v, dist[v])); } }}void DFS(int root, int cur, double MM){ EdgeM[root][cur] = MM; vis[cur] = true; for(vector<int>::iterator it = adj[cur].begin(); it != adj[cur].end(); it++) if(!vis[*it]) DFS(root, *it, max(MM, edge[cur][*it]));}void Solve(){ for(int i = 1; i <= n; i++) { memset(vis, false, sizeof(vis)); DFS(i, i, 0); } double ans = -1; for(int i = 1; i <= n; i++) for(int j = 1; j < i; j++)//枚舉super的那條路 { if(flag[i][j]) ans = max(ans, (popu[i]+popu[j])/(sum-edge[i][j])); else ans = max(ans, (popu[i]+popu[j])/(sum-EdgeM[i][j])); } printf("%.2lf\n", ans);}int main(){ scanf("%d", &tcase); while(tcase--) { Init(); Prim(); Solve(); } return 0;}