poj2728 Desert King(最优比率生成树【Prim)

题目链接

分析:
最小化: costlen ∑ c o s t ∑ l e n

01分数规划:最优比率生成树
渠道的长度是两个村庄之间的水平距离,通道的成本是升降机的高度

题目约定:
He just needs to build the necessary channels to bring water to all the villages, which means there will be only one way to connect each village to the capital

那么我们就需要找到一棵生成树

设函数 F(L)=cost[i]x[i]Llen[i]x[i]=(cost[i]Llen[i])x[i] F ( L ) = ∑ c o s t [ i ] ∗ x [ i ] − L ∗ ∑ l e n [ i ] ∗ x [ i ] = ∑ ( c o s t [ i ] − L ∗ l e n [ i ] ) ∗ x [ i ]

因为L越大d值越小
我们的目标是使 costlen ∑ c o s t ∑ l e n 尽量小
L>costlen,F(L)=cost[i]x[i]Llen[i]x[i]<0 L > ∑ c o s t ∑ l e n , F ( L ) = ∑ c o s t [ i ] ∗ x [ i ] − L ∗ ∑ l e n [ i ] ∗ x [ i ] < 0
F(L)<0 F ( L ) < 0 时,L的范围可以进一步缩小

二分答案,把每条边的边权设为 d[i]=cost[i]Llen[i] d [ i ] = c o s t [ i ] − L ∗ l e n [ i ] ,之后用最小生成树判断
因为是稠密图(完全图),推荐使用Prim算法计算生成树

没写过Prim?一学就会!

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>

using namespace std;

const int N=1002;
const double eps=1e-6;
const double INF=1e10;
double cost[N][N],dis[N][N],d[N][N];
int n,m;
struct node{
    double x,y,z;
};
node a[N];

double Dis(int i,int j) {
    return 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));
}

bool vis[N];
double v[N];

int check(double x) {
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++) 
            d[i][j]=d[j][i]=cost[i][j]-x*dis[i][j];

    double sum=0;
    int cur=1;
    vis[1]=1; 
    v[1]=0.0;    //到结点i的最短边 
    for (int i=2;i<=n;i++) vis[i]=0,v[i]=INF;

    for (int o=1;o<n;o++) {
        double mn=INF;
        int k;
        for (int i=1;i<=n;i++) 
            if (!vis[i]) {
                if (d[cur][i]<v[i]) v[i]=d[cur][i];
                if (v[i]<mn) mn=v[i],k=i;
            }
        vis[k]=1;
        sum+=mn;
        cur=k;
    }

    return sum<=0;
}

int main() 
{
    while (scanf("%d",&n)!=EOF&&n) 
    {
        double l=0,r=0,ans=INF;

        for (int i=1;i<=n;i++) scanf("%lf%lf%lf",&a[i].x,&a[i].y,&a[i].z);
        for (int i=1;i<=n;i++) 
            for (int j=i+1;j<=n;j++) {
                cost[i][j]=cost[j][i]=fabs(a[i].z-a[j].z);
                dis[i][j]=dis[j][i]=Dis(i,j);

                r=max(r,cost[i][j]/dis[i][j]);
            }

        while (r-l>=eps) {
            double mid=(l+r)/2;
            if (check(mid)) ans=min(ans,mid),r=mid;
            else l=mid;
        }
        printf("%0.3lf\n",ans);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值