zoukankan      html  css  js  c++  java
  • 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
    ",ans);
        }
    }
  • 相关阅读:
    叶问14
    叶问13
    叶问12
    叶问11
    叶问10
    叶问9
    Java三种循环之间的区别
    利用Java对象数组制作简易学生管理系统
    什么叫java方法重载?
    Java编译器的常量优化
  • 原文地址:https://www.cnblogs.com/huangchenyan/p/11265422.html
Copyright © 2011-2022 走看看