POJ 2728 Desert King(最优比例生成树)

来源:互联网 发布:广州seo外包eiaike 编辑:程序博客网 时间:2024/05/15 17:21

题目大意

有n个村庄,村庄在不同坐标和海拔,现在要对所有村庄供水,只要两个村庄之间有一条路即可,建造水管距离为坐标之间的欧几里德距离,费用为海拔之差,现在要求方案使得费用与距离的比值最小,很显然,这个题目是要求一棵最优比率生成树。

解法

有带权图G, 对于图中每条边e[i], 都有benefiti和costi, 我们要求的是一棵生成树T, 它使得 ∑(benefit[i]) / ∑(cost[i]), i∈T 最大(或最小).
设x[i]等于1或0, 表示边e[i]是否属于生成树.
则我们所求的比率 r = ∑(benefit[i] * x[i]) / ∑(cost[i] * x[i]), 0≤ i < m .
为了使 r 最大, 设计一个子问题—> 让 z = ∑(benefit[i] * x[i]) - l * ∑(cost[i] * x[i]) = ∑(d[i] * x[i]) 最大 (d[i] = benefit[i] - l * cost[i]) , 并记为z(l). 我们可以兴高采烈地把z(l)看做以d为边权的最大生成树的总权值.
然后明确两个性质:
 1. z单调递减
  证明: 因为cost为正数, 所以z随l的减小而增大.
 2. z( max(r) ) = 0
  证明: 若z( max(r) ) < 0, ∑(benefit[i] * x[i]) - max(r) * ∑(cost[i] * x[i]) < 0, 可化为 max(r) < max(r). 矛盾;
若z( max(r) ) >= 0, 根据性质1, 当z = 0 时r最大.
转自zhang20072844
但是上面讲得很清楚了,但是我依然理解了很久,首先我说明本题为什么找的是最小生成树。假设∑(benefit[i])/∑(cost[i])=min,我们知道d[i]是每条边的边权,假如说存在一个更优的d[j]使∑(benefit[i])/∑(cost[i])< min,很明显d[j]要小于d[i],于是本题很明显应该找最小生成树才能找到最小的比例。

#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>const int maxn = 1005;const double inf = 0x3f3f3f3f;const double eps = 1e-5;int vis[maxn],x[maxn],y[maxn],z[maxn],pre[maxn];double dis[maxn],cost[maxn][maxn],dist[maxn][maxn];int n;double prim(double rate){    double sum = 0.0;    for (int i = 1; i <= n; i++) pre[i] = 1, dis[i] = inf;    dis[1] = 0;    memset(vis, 0, sizeof(vis));    for (int i = 1; i <= n; i++){        double Min = inf;        int tmp;        for (int j = 1; j <= n; j++)            if (!vis[j] && dis[j] < Min)                Min = dis[tmp = j];        vis[tmp] = 1;        sum += Min;        for (int j = 1; j <= n; j++)            if (dis[j] > cost[tmp][j] - dist[tmp][j]*rate){                dis[j] = cost[tmp][j] - dist[tmp][j]*rate;                pre[j] = tmp;            }    }    return sum;}int main(){    while(scanf("%d", &n) && n){        for(int i = 1; i <= n; i++){            scanf("%d%d%d", &x[i], &y[i], &z[i]);            for (int j = 1; j < i; ++j){                double tmp = (x[i] - x[j])*(x[i] - x[j]) + (y[i] - y[j])*(y[i] - y[j]);                cost[i][j] = cost[j][i] = abs(z[i] - z[j]);                dist[i][j] = dist[j][i] = sqrt(tmp);            }        }        double l = 0,r = 1000000.0;        while (r - l > eps){            double mid = (l + r)/2.0;            if (prim(mid) >= 0) l = mid;            else r = mid;        }        printf("%.3f\n", r);    }    return 0;}
0 0