最优比率生成树

在这里插入图片描述

题目大意

题目要求我们求成本和总长度的比值的最小值。
这是一道0/1分数规划问题。

分析:

我们首先需要构建一个完全图,在该完全图上求满足题目要求的树。
假设树中每一段路径的成本是ai ,长度是bi ,则比值为 ( ∑ i = 1 n a ) / ( ∑ i = 1 n b ) (\sum_{i=1}^{n}a)/(\sum_{i=1}^{n}b) (i=1na)/(i=1nb)
我们要做的就是求该式子的最小值,可以用二分来求最小值。
假设当前二分的值为mid,我们只需要判断 ( ∑ i = 1 n a ) / ( ∑ i = 1 n b ) < = m i d (\sum_{i=1}^{n}a)/(\sum_{i=1}^{n}b)<=mid (i=1na)/(i=1nb)<=mid是否成立。
如果成立,我们就让r=mid,否则l=mid。

将上式变形: ( ∑ i = 1 n a ) < = m i d ∗ ( ∑ i = 1 n b ) (\sum_{i=1}^{n}a)<=mid*(\sum_{i=1}^{n}b) (i=1na)<=mid(i=1nb)
继续变形: ( ∑ i = 1 n a ) − m i d ∗ ( ∑ i = 1 n b ) < = 0 (\sum_{i=1}^{n}a)-mid*(\sum_{i=1}^{n}b)<=0 (i=1na)mid(i=1nb)<=0
所以我们只需要在check时,将边权变为成本-mid*路径长度,构建最小生成树,判断最后的最小生成树的边权之和是否小于等于0即可。

代码:
//构建完全图
//构建最小生成树
//二分
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
const double eps=1e-8;
double g[N][N],d[N][N];
double dis[N];
int n;
int st[N];
struct Node{
    int x,y,h;
    void read(){
        cin>>x>>y>>h;
    }
    double get_dis(Node b){
        double dx=x-b.x,dy=y-b.y;
        return sqrt(dx*dx+dy*dy);
    }
}cun[N];
bool check(double x){
    double res=0;
    double gg=0,dd=0;
    memset(st,0,sizeof st);
    for(int i=1;i<=n;i++)dis[i]=1e9;
    dis[1]=0;
    for(int i=1;i<=n;i++){
        //找不在集合内且距离集合最小的点
        int t=-1;
        for(int j=1;j<=n;j++){
            if(!st[j]&&(t==-1||dis[j]<dis[t]))t=j;
        }
        st[t]=1;
        res+=dis[t];
        for(int i=1;i<=n;i++){
            if(!st[i]){
                dis[i]=min(dis[i],g[t][i]-x*d[t][i]);
            }
        }
    }
    return res<=0;
}
void solve(){
    for(int i=1;i<=n;i++)cun[i].read();
    //构建完全图
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            g[i][j]=abs(cun[i].h-cun[j].h);
            d[i][j]=cun[i].get_dis(cun[j]);
        }
    }
    //二分
    double l=0,r=1e12;
    while(fabs(l-r)>eps){
        double mid=(l+r)/2;
        if(check(mid))r=mid;
        else l=mid;
    }
    printf("%.3lf\n",l);
}
int main(){
    while(cin>>n){
        if(n==0)break;
        solve();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值