1. 程式人生 > >最小生成樹求最大比率 UVALive - 5713

最小生成樹求最大比率 UVALive - 5713

題目連結:https://vjudge.net/problem/UVALive-5713

題意:給出t組資料,每組資料第一行給出一個n,表示點的數量,接下來n行,每行有三個數字,分別是點的座標x,y和點的值w。現在我們要用n-1條邊來連線這n個點,秦始皇希望這n-1條邊的權值之和最小,現在徐福說他可以讓其中一條邊的權值為0,但是他希望這條邊所連線的兩點的值之和最大,所以秦始皇決定使A/B的值最大,其中,A是徐福選擇的那條邊所連線兩點的值的和,B是除徐福選擇的邊之外,其他n-2條邊的權值之和,輸出最大的A/B。

以下內容是書上面看的。

思路:要使A/B最大,所以A要儘量大,B要儘量小。我們先讓B儘量小,所以我們先求出最小生成樹的權值之和假設是sum,那麼我們只要列舉最小生成樹的每一條邊,把這條邊去掉,假設這條邊權值是s,讓它變成兩棵樹,分別在兩顆樹裡面找到值最大的點(假設兩個點的值分別是a,b),結果就是max{(a+b)*1.0/(sum-s)}。

對於sum,我們用prime演算法計算出來,同時記錄每個點的前驅,然後列舉我們所要去掉的邊的時候,我們可以用dfs標記,把最小生成樹分成兩顆樹。

看程式碼吧:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<set>
#include<cstdio>
#include
<string> #include<deque> using namespace std; typedef long long LL; #define eps 1e-8 #define INF 0x3f3f3f3f #define maxn 1005 int n,m,k,t; double map[maxn][maxn],dis[maxn]; int pre[maxn]; double sum; int vis[maxn]; struct node{ double x,y; int w; }point[maxn]; double cal_dis(int i,int
j){//計算兩點距離 return sqrt(1.0*(point[i].x-point[j].x)*(point[i].x-point[j].x)+(point[i].y-point[j].y)*(point[i].y-point[j].y)); } void prime(){ memset(vis,0,sizeof(vis)); memset(pre,0,sizeof(pre)); for(int i=1;i<=n;i++) dis[i]=INF; dis[1]=0; sum=0; for(int i=1;i<=n;i++){ double min1=INF; int u; for(int j=1;j<=n;j++){ if(!vis[j]&&dis[j]<min1){ min1=dis[j]; u=j; } } if(min1==INF) return; vis[u]=1; sum+=min1; for(int j=1;j<=n;j++){ if(!vis[j]&&map[u][j]<dis[j]){ dis[j]=map[u][j]; pre[j]=u;//記錄點j的前驅 } } } } void DFS(int u){ vis[u]=1; for(int i=1;i<=n;i++){ if(!vis[i]&&pre[i]==u) DFS(i); } } double solve(){ double ans=0; for(int i=1;i<=n;i++){ if(pre[i]==0)//我們選擇連線點i和點i的前驅的這條邊,如果點i沒有前驅continue continue; memset(vis,0,sizeof(vis)); DFS(i);//標記以點i根節點的樹(標記為1) double s=map[i][pre[i]];//記錄去掉的這條邊的權值 int a=0,b=0; for(int j=1;j<=n;j++){ if(vis[j])//屬於以點i為根的樹 a=max(a,point[j].w); else//屬於以點pre[i]為根的樹 b=max(b,point[j].w); } ans=max(ans,(a+b)*1.0/(sum-s)); } return ans; } int main() { scanf("%d",&t); while(t--){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lf%lf%d",&point[i].x,&point[i].y,&point[i].w); memset(map,0,sizeof(map)); for(int i=1;i<n;i++){ for(int j=i+1;j<=n;j++){ map[i][j]=map[j][i]=cal_dis(i,j); } } prime(); printf("%.2lf\n",solve()); } return 0; }