题目链接:http://acm.pku.edu.cn/JudgeOnline/problem?id=2728

和3757一样都是01分数规划的题,不同的是3757是用的二分,这里用的是Prim

0-1背包部分和3757一样

令m(l) = min{∑(1.0 * h[i][j] - l * dis[i][j] )}

h[i][j]是i到j的高度差,dis是距离,把m(l)作为边的权值(黑书上有)

由于m(l)为相对于l的单调函数,令l为最优值,m(l) = 0时的l*为所求

而对于其他的l‘,对于min{ ∑(1.0 * h[i][j] - l * dis[i][j] ) }中的边,令∑(1.0 * h[i][j] - l' * dis[i][j] ) = 0

| l*-l' | < | l* - l |   ,所以可用迭代法来做这题

(关于分数规划见《IOI国家集训队论文》07年胡伯涛论文中的《1.6. 分数规划》部分)

代码:

/**
* 2728 Desert King
* URL: http://acm.pku.edu.cn/JudgeOnline/problem?id=2728
* Author: OWenT
* Blog: http://www.owent.net
* 0-1分数规划 + Prim最小生成树 + 迭代法
*/

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;

#define MAXN 1005
double minEle[MAXN], dis[MAXN][MAXN];
long x[MAXN], y[MAXN], z[MAXN], alt[MAXN][MAXN], minFrom[MAXN];

double primEX(double mat[MAXN][MAXN],const long &n, double rv);//拓展Prim,对于这题的专用修改版Prim算法

int main()
{
    long i, j, n;
    double res, resC;
    while(scanf("%ld", &n), n)
    {
        for(i = 0; i < n; i ++)
        {
            scanf("%ld %ld %ld", &x[i], &y[i], &z[i]);
            for(j = 0; j < i; j ++)
            {
                double tmp = (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]);
                alt[i][j] = alt[j][i] = abs(z[i] - z[j]);
                dis[i][j] = dis[j][i] = sqrt(tmp);
            }
        }

        //init
        res = 0;//从0开始迭代
        do
        {
            resC = res;
            res = primEX(dis, n, res);
        }while(fabs(res - resC) > 1e-5);

        printf("%.3lf\n", res);
    }
    return 0;
}

double primEX(double mat[MAXN][MAXN],const long &n, double rv)
{
    double hs = 0, rs = 0;
    long i, j, pos;
    memset(minFrom, 0, sizeof(minFrom));
    for(i = 1; i < n; i ++)
        minEle[i] = 1.0 * alt[0][i] - rv * mat[0][i];
    minFrom[0] = -1;
    for(i = 1; i < n; i ++)
    {
        pos = 0;
        for(j = 1; j < n; j ++)
            if(minFrom[j] >= 0 && ( pos == 0 || minEle[j] < minEle[pos]))
                pos = j;
        rs += mat[minFrom[pos]][pos];
        hs += alt[minFrom[pos]][pos];
        minFrom[pos] = -1;
        for(j = 1; j < n; j ++)
            if(minFrom[j] >= 0 && minEle[j] > 1.0 * alt[pos][j] - rv * mat[pos][j])
                minEle[j] = 1.0 * alt[pos][j] - rv * mat[pos][j], minFrom[j] = pos;
    }

    return hs / rs;
}