POJ 2728 Desert King

0/1分数规划+最小生成树

题意

有n个村庄,村庄在不同坐标和海拔,现在要对所有村庄供水,只要两个村庄之间有一条路即可,建造水管距离为坐标之间的欧几里德距离,费用为海拔之差,现在要求方案使得费用与距离的比值最小

n <= 1000

输入n行每行三个数x y z分别表示坐标与海拔

 

题目中所给条件,可以推出任意两点间的距离和海拔差

记$d[i][j]$表示从点i到点j的距离,$cost[i][j]$表示从点i到点j的海拔之差

对于距离$d$数组,海拔$cost$数组,可以看做一个典型的0/1分数规划

根据0/1分数规划的做法,二分枚举答案

但是二分中判断函数,不只是单单的排序

记$e[i][j]=h[i][j]-mid*cost[i][j]$

那么判断函数就是求以e数组为图的边的最小生成树

此处为完全图,Kruskal算法复杂度为$O(n^{2}logn^{2})$,外层二分再套个$logn$

复杂度无法接受

那么使用Prim算法

那么总复杂度为$O(n^{2}logn)$

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
int n,cost[1100][1100];
double dis[1100][1100],e[1100][1100],ans;
double lowcost[1100];
bool vi[1100];
struct node
{
    int x,y,z;
}sh[1100];
bool check(double mid)
{
    double sum=0.0;
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n;j++)
        {
            if (i==j)
            {
                e[i][j]=0;
                continue;
            }
            e[i][j]=(double)cost[i][j]-mid*dis[i][j];//求出e数组
        }
        vi[i]=0;
    }
    for (int i=2;i<=n;i++)
    {
        lowcost[i]=e[1][i];
    }
    vi[1]=1;
    for (int i=1;i<n;i++)//Prim算法
    {
        double MIN;
        int wh;
        MIN=1.0e9;
        for (int j=2;j<=n;j++)
        {
            if (!vi[j] && lowcost[j]<MIN)
            {
                MIN=lowcost[j];
                wh=j;
            }
        }
        vi[wh]=1;
        sum+=MIN;
        for (int j=2;j<=n;j++)
        {
            if (!vi[j] && e[wh][j]<lowcost[j])
            {
                lowcost[j]=e[wh][j];
            }
        }
    }
    return sum>=0.0;
}
int main()
{
    while (1)
    {
        scanf("%d",&n);
        if (n==0)
          break;
        for (int i=1;i<=n;i++)
          scanf("%d%d%d",&sh[i].x,&sh[i].y,&sh[i].z);
        for (int i=1;i<=n;i++)
        {
            for (int j=1;j<=n;j++)
            {
                cost[i][j]=abs(sh[i].z-sh[j].z);
                dis[i][j]=sqrt((sh[i].x-sh[j].x)*(sh[i].x-sh[j].x)+(sh[i].y-sh[j].y)*(sh[i].y-sh[j].y));
            }
        }
        double l,r,mid;
        l=0.0;
        r=1000000000.0;
        while (r-l>1.0e-6)//注意精度问题
        {
            mid=(l+r)/2;
            if (check(mid))
            {
                l=mid;
                ans=mid;
            }
            else
            {
                r=mid;
            }
        }
        printf("%.3f\n",ans);
    }
}

 

posted @ 2019-07-29 17:46  SevenDawns  阅读(173)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end