HDU-#4081 Qin Shi Huang's National Road System(次小生成树变形)

本文探讨了一个涉及次小生成树的概念及其在路径选择优化问题中的应用。通过利用次小生成树的特性,文章提出了一个有效的算法来解决在特定约束条件下的路径最优化问题,特别关注于如何通过合理选择路径来最大化特定路径比值,从而实现资源的最优分配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

         题目大意:有n个城市,秦始皇要修n-1条路连接,要求路径最短。而徐福拥有魔法可以修一条魔法道路,不需要人力和财力花费。但是这条路是人力最多的那一条。因此秦始皇给出了一个公式A/B,A为那条路两边城市的人数,B为除这条路其它要修路的路径之和。

         解题思路:之前没写过次小生成树,以为不好写,看了题解之后发现不是很难的,主要是怎么去想吧!想好了就会很简单,想不到就会很难,参见了这篇博客的解法。具体的解法是:要使的A/B值最大,则需要是B尽量要小。但需要求减少哪一条边,则可以进行枚举删边,来更新答案,最后枚举所有的边后输出即可。就是次小生成树的解法。这里要注意的是可能魔法路不是MST的边,这是需要删除MST中两个点之间权值最大的哪一条边,因此Prim时要进行记录两个点之间权值最大的哪一条边。详见cod。

        题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=4081

        code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;

const int MAXN = 1000+10;
const int INF = 0x3fffffff;
int t,n;
int pre[MAXN],vis[MAXN],people[MAXN]; //pre存储当前结点的前驱结点
double dist[MAXN],g[MAXN][MAXN],path[MAXN][MAXN]; //path存储从i到j的路径上的最大权值
bool used[MAXN][MAXN]; //记录两个结点之间的路径是否在MST中

struct point{
    int x,y;
}p[MAXN];

double distan(point p1,point p2){ //距离计算公式
    return sqrt(double(p1.x-p2.x)*(p1.x-p2.x)+double(p1.y-p2.y)*(p1.y-p2.y));
}

double prim(){
    double ans=0;
    memset(vis,0,sizeof(vis));
    memset(path,0,sizeof(path));
    memset(used,0,sizeof(used));
    vis[1]=1;
    for(int i=1;i<=n;++i){ //初始化
        dist[i]=g[1][i];
        pre[i]=1; //前驱结点均为1
    }
    for(int i=1;i<n;++i){
        int u=-1;
        for(int j=1;j<=n;++j)
            if(!vis[j] && (u==-1 || dist[j]<dist[u])) u=j; //求最小权值边
        used[u][pre[u]]=used[pre[u]][u]=true; //标记最小生成树的边
        ans+=g[pre[u]][u]; //记录最小值
        vis[u]=1; //访问标记
        for(int k=1;k<=n;++k){
            if(vis[k] && k!=u) //计算u到k之间的权值最大的边权值
                path[u][k]=path[k][u]=max(path[k][pre[u]],dist[u]);
            if(!vis[k] && dist[k]>g[u][k]){ //更新最小边
                dist[k]=g[u][k];
                pre[k]=u; //记录前驱结点
            }
        }
    }
    return ans;
}

int main(){
    //freopen("input.txt","r",stdin);
    scanf("%d",&t);
    while(t--){
        memset(g,0,sizeof(g));
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
            scanf("%d%d%d",&p[i].x,&p[i].y,&people[i]);
        for(int i=1;i<=n;++i) //算权值,建图
            for(int j=1;j<=n;++j)
                if(i!=j) g[i][j]=distan(p[i],p[j]);
        double mst=prim();
        double ans=-1;
        for(int i=1;i<=n;++i) //枚举
            for(int j=1;j<=n;++j)
                if(i!=j){
                    if(used[i][j]) //如果在MST中
                        ans=max(ans,(people[i]+people[j])/(mst-g[i][j]));
                    else
                        ans=max(ans,(people[i]+people[j])/(mst-path[i][j]));
                }
        printf("%.2f\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值