kuangbin专题八 HDU4081 Qin Shi Huang's National Road System(次小生成树)

本文介绍了一个有趣的次小生成树问题,背景设定在古代中国,秦始皇希望通过修路将各个城市连接起来,并利用徐福的魔法来优化修建成本。通过枚举每条边寻找最优解,确保人力与物力的最佳分配。

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

题意:
有n个城市,秦始皇要修用n-1条路把它们连起来,要求从任一点出发,都可以到达其它的任意点。秦始皇希望这所有n-1条路长度之和最短。然后徐福突然有冒出来,说是他有魔法,可以不用人力、财力就变出其中任意一条路出来。
秦始皇希望徐福能把要修的n-1条路中最长的那条变出来,但是徐福希望能把要求的人力数量最多的那条变出来。对于每条路所需要的人力,是指这条路连接的两个城市的人数之和。
最终,秦始皇给出了一个公式,A/B,A是指要徐福用魔法变出的那条路所需人力, B是指除了徐福变出来的那条之外的所有n-2条路径长度之和,选使得A/B值最大的那条。
题解:
次小生成树稍微改一下,因为要让A/B最大,肯定要枚举每条边找到最优,当加入的边是最小生成树外的就-path[i][j],如果是树内的就-map[i][j]即可。麻痹,这道题有一个点坑了我一上午,就是在枚举次小生成树的时候i不能等于j,为什么?如果会出现最大的人口的点相加两次再去除的,这样是不对的!过了这样点之后终于看到绿色了。。。。。也学到了原来INF*1.0就可以转化成double。

//有点挫的代码
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
const int MAXN=1005;
struct node
{
    double x,y,w;
}a[MAXN];
double map[MAXN][MAXN];
double dis[MAXN];
double path[MAXN][MAXN];
int pre[MAXN];
int stack[MAXN];
bool vis[MAXN];
int n,cnt; 
double prim()
{
    double sum=0;
    memset(vis,false,sizeof(vis));
    memset(pre,0,sizeof(pre));
    memset(stack,0,sizeof(stack));
    for(int i=1;i<=n;i++)
    dis[i]=map[1][i],pre[i]=1;
    vis[1]=true;
    stack[cnt++]=1;
    for(int i=1;i<=n;i++)
    {
        double MIN=1000000000;
        int k=0;
        for(int j=1;j<=n;j++)
        if(!vis[j]&&dis[j]<MIN)
        MIN=dis[j],k=j;
        vis[k]=true;
        for(int j=0;j<cnt;j++)
        path[stack[j]][k]=path[k][stack[j]]=max(path[stack[j]][pre[k]],MIN);
        stack[cnt++]=k;
        for(int j=1;j<=n;j++)
        if(!vis[j]&&map[k][j]<dis[j]){
            dis[j]=map[k][j];
            pre[j]=k;
        }
    }
    for(int i=1;i<=n;i++) sum+=dis[i];
    return sum;
}
int main() 
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        cnt=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        scanf("%lf%lf%lf",&a[i].x,&a[i].y,&a[i].w);
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            if(i==j)
            map[i][j]=0;
            else
            map[i][j]=sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y));
        }
        double mst=prim();  
        double ans=0;
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            if(i==j)
            continue;
            if(i!=pre[j]&&j!=pre[i])
            ans=max(ans,(a[i].w+a[j].w)/(mst-path[i][j]));
            else
            ans=max(ans,(a[i].w+a[j].w)/(mst-map[i][j]));           
        }
        printf("%0.2lf\n",ans);
    }
}
//这个代码好用一点,时间也快点
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
const int MAXN=1005;
struct node
{
    double x,y,w;
}a[MAXN];
double map[MAXN][MAXN];
double path[MAXN][MAXN];
double dis[MAXN];
int pre[MAXN];
bool vis[MAXN];
bool used[MAXN][MAXN];
int n;
double prim()
{
    double sum=0;
    memset(pre,0,sizeof(pre));
    memset(vis,false,sizeof(vis));
    memset(path,0,sizeof(path));
    memset(used,false,sizeof(used));
    for(int i=1;i<=n;i++)
    dis[i]=map[1][i],pre[i]=1;
    vis[1]=true;
    for(int i=1;i<=n;i++)
    {
        double MIN=INF*1.0;
        int k=0;
        for(int j=1;j<=n;j++)
        if(!vis[j]&&MIN>dis[j])
        MIN=dis[j],k=j;
        vis[k]=true;
        used[k][pre[k]]=used[pre[k]][k]=true;
        for(int j=1;j<=n;j++)
        {
            if(vis[j]&&j!=k)//这里一定要有j!=k,如果两者相等的话,path[j][pre[j]]与MIN比较,从而导致path[i][i]不等于0的。导致破坏了最优状态 
            path[k][j]=path[j][k]=max(path[j][pre[k]],MIN);
            if(!vis[j]&&map[k][j]<dis[j])
            dis[j]=map[k][j],pre[j]=k;
        } 
    }
    for(int i=1;i<=n;i++) sum+=dis[i];
    return sum;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        scanf("%lf%lf%lf",&a[i].x,&a[i].y,&a[i].w);
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        if(i==j)
        map[i][j]=0;
        else
        map[i][j]=sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y));
        double mst=prim();
        double ans=-1;
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            if(i==j)
            continue;
            if(!used[i][j])
            ans=max(ans,(a[i].w+a[j].w)/(mst-path[i][j]));
            else
            ans=max(ans,(a[i].w+a[j].w)/(mst-map[i][j]));           
        }
        printf("%.2lf\n",ans);      
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值