acm專題學習之生成樹(二)次小生成樹+HDU-4081

題目:秦始皇想要修路,他只想着修總長度最小(最小生成樹)。但有個道士告訴他,可以用魔法修一條路,不耗費人力財力的一條魔法路。秦始皇想要使非魔法道路總長度儘可能小,道士希望儘可能多的人受益。最終決定:修魔法路的兩個城市,(兩個城市的總人口)/(非魔法道路總長度)必須是最大。

輸入:t個測試數據,n個城市,每個城市輸入爲座標(x,y)和人口p

輸出:(兩個城市的總人口)/(非魔法道路總長度)最大值

次小生成樹:在最小生成樹的基礎上,枚舉每一條不在最小生成樹上的邊,把邊放到最小生成樹上,然後就會形成環路,去掉環路上除了新加入的邊的最長邊,根據附加的條件得到要的答案。

具體實現:除了基本的最小生成樹,還有加入connect數組用來記錄不屬於最小生成樹的連通邊(方便枚舉每一條不在最小生成樹上的邊),maxd數組記錄最小生成樹中i點到j點中最大的距離(方便後面刪去環路上除了新加入的邊的最長邊),per數組記錄最小生成樹裏面每個點的上一個鏈接的點(方便得到父座標)

代碼:

#include <algorithm>
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn=1005;
const int inf=1e9+9;
int n;
bool vis[maxn];//記錄最小生成樹集合
double dis[maxn];//更新最小生成樹集合到其他點的距離
double maxd[maxn][maxn];//maxd[i][j]表示最小生成樹裏i點到j點中最大的距離
int per[maxn];//最小生成樹裏面每個點的上一個鏈接的點
double connect[maxn][maxn];//記錄是否是通路,且該通路不在最小生成樹裏面
struct nod
{
    double x,y;
    double p;
} num[maxn];
double calculate(int i,int j)//計算兩點的距離
{
    return sqrt((num[i].x-num[j].x)*(num[i].x-num[j].x)+(num[i].y-num[j].y)*(num[i].y-num[j].y));
}
double Prim()
{
    memset(maxd,0,sizeof(maxd));
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++)//選第一個點
    {
        dis[i]=calculate(1,i);
        per[i]=1;
    }
    vis[1]=1;
    double sum=0;
    for(int i=1;i<n;i++)
    {
        double min1=inf;
        int p=-1;
        for(int j=1;j<=n;j++)//找出與最小生成樹當前集合相連的最小距離的點
        {
            if(!vis[j]&&dis[j]<min1)
            {
                p=j;
                min1=dis[j];
            }
        }
        if(p==-1)//如果找不到了,就說明已經找完了
            return sum;
        vis[p]=1;
        //把這條邊從通路中刪除,保留非生成樹通路
        connect[p][per[p]]=false;
        connect[per[p]][p]=false;
        sum+=min1;
        maxd[per[p]][p]=maxd[p][per[p]]=min1;
        for(int j=1;j<=n;j++)
        {
            if(j!=p&&vis[j])
            {
                maxd[p][j]=maxd[j][p]=max(maxd[j][per[p]],dis[p]);
            }
            if(!vis[j]&&calculate(j,p)<dis[j])
            {
                dis[j]=calculate(j,p);
                per[j]=p;
            }
        }
    }
    return sum;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        //memset(mp,inf,sizeof(mp));
        memset(connect,true,sizeof(connect));
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
        {
            scanf("%lf%lf%lf",&num[i].x,&num[i].y,&num[i].p);
        }
        double sum=Prim();
        double ans=-1;
        for(int i=1;i<=n;i++)
        {
            for(int j=i+1;j<=n;j++)
            {
                if(connect[i][j])
                {
                    ans=max(ans,1.0*(num[i].p+num[j].p)/(sum-maxd[i][j]));
                }
                else
                {
                    ans=max(ans,1.0*(num[i].p+num[j].p)/(sum-calculate(i,j)));
                }
            }
        }
        printf("%.2lf\n",ans);
    }
    return 0;
}

總結:1 學到了新東西 2 看英文題的時候,不要太依賴網頁翻譯工具,還是要自己看看英文。(這次網頁翻譯出錯,導致一直看錯題)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章