题目描述:

David the Great has just become the king of a desert country. To win the respect of his people, he decided to build channels all over his country to bring water to every village. Villages which are connected to his capital village will be watered. As the dominate ruler and the symbol of wisdom in the country, he needs to build the channels in a most elegant way.

After days of study, he finally figured his plan out. He wanted the average cost of each mile of the channels to be minimized. In other words, the ratio of the overall cost of the channels to the total length must be minimized. 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.

His engineers surveyed the country and recorded the position and altitude of each village. All the channels must go straight between two villages and be built horizontally. Since every two villages are at different altitudes, they concluded that each channel between two villages needed a vertical water lifter, which can lift water up or let water flow down. The length of the channel is the horizontal distance between the two villages. The cost of the channel is the height of the lifter. You should notice that each village is at a different altitude, and different channels can't share a lifter. Channels can intersect safely and no three villages are on the same line.

As King David's prime scientist and programmer, you are asked to find out the best solution to build the channels.

Input

There are several test cases. Each test case starts with a line containing a number N (2 <= N <= 1000), which is the number of villages. Each of the following N lines contains three integers, x, y and z (0 <= x, y < 10000, 0 <= z < 10000000). (x, y) is the position of the village and z is the altitude. The first village is the capital. A test case with N = 0 ends the input, and should not be processed.

Output

For each test case, output one line containing a decimal number, which is the minimum ratio of overall cost of the channels to the total length. This number should be rounded three digits after the decimal point.

Sample Input

4
0 0 0
0 1 1
1 1 2
1 0 3
0

Sample Output

1.000

题意:有一张无向图,每条边有费用值和长度值,要求找到一颗生成树,使得总费用/总长度最小
问题模型:最优比率生成树
解题方法:
设r=Σc[i]*x[i]/(Σl[i]*x[i])(x[i]=0/1)
目标,最小化r
变形得:Σc[i]*x[i]-r*Σl[i]*x[i]=0
设f[r]=Σc[i]*x[i]-r*Σl[i]*x[i]于是对于每一组x[i],以我们得到一条直线,r是这条直线的横截距(确定x[i]之后其它的都是常数)我们的目标变成,找到全部直线中的最小横截距怎么找呢?首先暴力枚举x[i]绝对是不可取的。我们发现其实只需要二分r的值,然后观察min(f[r])就好,如果min(f[r])还要小于0的话,我们就可以知道r>r*(r*是我们最后的所求值),反之r<r*当我们我们发现min(f[r])==0时,我们认为r=r*然后就直接输出考虑一下二分的时间复杂度,完全没有问题(注意这里是最大化r,包括下面的图,最小化是同理的)


(图片来自大佬ztx的CSDN博客,https://blog.csdn.net/hzoi_ztx/article/details/54898323,感谢大佬)
然而,我们还有一种方法(DinkelbachDinkelbach算法)
基本思想,其实和二分有点像,但它是基于迭代的。我们考虑如上述二分,我们有一个初始值r,
然后我们发现min(f[r])还要小于0,
那我们就直接把r转移到min(f[r])所代表的直线的横截距上。读者细细想想
就知道,不可能得到一个比r*还要小的值,因为r*就是最小的截距了。
于是我们只需要不断迭代转移r就好了。 咳咳,差点忘了。怎么求min(f[r]),其实很显然,为了得到min(f[r])的x[i],
我们得把边的长度按照上面f函数的计算方法改一改跑最小生成树。
为什么呢?
首先这样我们肯定可以得到一组x[i]
同时最小就是保证了我们要算的f的取值最小
代码里是堆优化的prim(我懒。。。不手写堆的)
下面附上我的DinkelbachDinkelbach代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<memory.h>
#include<queue>
#include<math.h>
using namespace std; const int maxn=1000+15;
int n,tot;
int head[maxn],in[maxn];
struct VILLAGE
{
double x;double y;double z;
}v[maxn];
struct EDGE
{
int from;int to;int next;double len;double cost;double e;
}edge[maxn<<12];
struct NODE{
int x;double l;double c;double d;
};
bool operator < (const NODE a,const NODE b){
return a.l>b.l;
}
void init(){
memset(edge,0,sizeof(edge));
memset(head,0,sizeof(head));
tot=0;
}
void add(int x,int y,double len,double cost){
edge[++tot]=(EDGE){x,y,head[x],len,cost,0};
head[x]=tot;
}
double prim(double oo)
{
memset(in,0,sizeof(in));
for (int i=1;i<=tot;i++)
{
edge[i].e=edge[i].cost-oo*edge[i].len;
//printf("%d %d %lf\n",edge[i].from,edge[i].to,edge[i].e);
}
priority_queue<NODE> q;
q.push((NODE){1,0,0,0});
int cnt=0;
double c1=0,d1=0;
while (!q.empty()&&cnt<n)
{
NODE k=q.top();q.pop();
if (in[k.x]) continue;
in[k.x]=1;
c1+=k.c;d1+=k.d;cnt++;
for (int i=head[k.x];i;i=edge[i].next)
{
int y=edge[i].to;
q.push((NODE){y,edge[i].e,edge[i].cost,edge[i].len});
}
}
//printf("\n%lf\n",c1/d1);
return c1/d1;
}
int main()
{
while (1)
{
scanf("%d",&n);
if (n==0) break;
init();
for (int i=1;i<=n;i++)
scanf("%lf%lf%lf",&v[i].x,&v[i].y,&v[i].z);
for (int i=1;i<n;i++)
for (int j=i+1;j<=n;j++)
{
double dis=sqrt((v[i].x-v[j].x)*(v[i].x-v[j].x)+(v[i].y-v[j].y)*(v[i].y-v[j].y));
double cost=fabs(v[i].z-v[j].z);
add(i,j,dis,cost);
add(j,i,dis,cost);
}
double r1=0.0,r2=0.0;
while (1){
r2=prim(r1);
if (fabs(r2-r1)<0.00001) break;
r1=r2;
}
printf("%.3f\n",r1);
}
return 0;
}

  解决了最小的问题,最大的问题不也就迎刃而解了,只需要找最大截距就好了。这是0/1分数规划的典型之一。

 其他的还有最优比率环什么的。
												

[POJ2728] Desert King 解题报告(最优比率生成树)的更多相关文章

  1. POJ2728 Desert King 【最优比率生成树】

    POJ2728 Desert King Description David the Great has just become the king of a desert country. To win ...

  2. 【最优比率生成树】poj2728 Desert King

    最优比率生成树教程见http://blog.csdn.net/sdj222555/article/details/7490797 个人觉得很明白易懂,但他写的代码略囧. 模板题,但是必须Prim,不能 ...

  3. poj2728 Desert King(最小生成树+01分数规划=最优比率生成树)

    题意 n个点完全图,每个边有两个权值,求分数规划要求的东西的最小值. (n<=1000) 题解 心态炸了. 堆优化primT了. 普通的就过了. 我再也不写prim了!!!! 咳咳 最优比率生成 ...

  4. POJ 2728 Desert King 最优比率生成树

    Desert King Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 20978   Accepted: 5898 [Des ...

  5. poj 2728 Desert King (最优比率生成树)

    Desert King http://poj.org/problem?id=2728 Time Limit: 3000MS   Memory Limit: 65536K       Descripti ...

  6. POJ 2728 Desert King ★(01分数规划介绍 && 应用の最优比率生成树)

    [题意]每条路径有一个 cost 和 dist,求图中 sigma(cost) / sigma(dist) 最小的生成树. 标准的最优比率生成树,楼教主当年开场随手1YES然后把别人带错方向的题Orz ...

  7. POJ 2728 Desert King(最优比率生成树 01分数规划)

    http://poj.org/problem?id=2728 题意: 在这么一个图中求一棵生成树,这棵树的单位长度的花费最小是多少? 思路: 最优比率生成树,也就是01分数规划,二分答案即可,题目很简 ...

  8. 最优比率生成树 poj2728

    Desert King Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 28407   Accepted: 7863 Desc ...

  9. [转]01分数规划算法 ACM 二分 Dinkelbach 最优比率生成树 最优比率环

    01分数规划 前置技能 二分思想最短路算法一些数学脑细胞? 问题模型1 基本01分数规划问题 给定nn个二元组(valuei,costi)(valuei,costi),valueivaluei是选择此 ...

随机推荐

  1. Java并发编程(七)ConcurrentLinkedQueue的实现原理和源码分析

    相关文章 Java并发编程(一)线程定义.状态和属性 Java并发编程(二)同步 Java并发编程(三)volatile域 Java并发编程(四)Java内存模型 Java并发编程(五)Concurr ...

  2. Socket实现一个简单的半双工通信

    Socket是client进行在网络与server进行数据交互的一种基本通信方式.通信有三种通信.即单工.半双工,和全双工. 所谓单工,就是仅仅可以进行单向通信,如bb机. 而半双工就是一来一回的通信 ...

  3. 关于Android的.so文件所须要知道的

    早期的Android系统差点儿仅仅支持ARMv5的CPU架构,你知道如今它支持多少种吗?7种. Android系统眼下支持以下七种不同的CPU架构:ARMv5.ARMv7 (从2010年起),x86 ...

  4. node14---分层结构数据库操作

    /**回调函数(函数作为参数): 0. 外层函数调用的地方,一定是外层函数体先执行,回调函数和普通函数地址一样,然后看函数体规定回调函数怎么执行. 1. 异步时候使用回调函数, 无论是否异步,回调函数 ...

  5. [hihocoder #1384] Genius ACM 解题报告(倍增)

    题目链接:http://hihocoder.com/problemset/problem/1384 题目大意: 给定一个整数 M,对于任意一个整数集合 S,定义“校验值”如下: 从集合 S 中取出 M ...

  6. windows共享如何重新登录,或用另外的用户登录

    使用net use * /del 可以结束已有的所有连接,或net use \\192.168.1.10  /del可以结束指定连接.比如想重新登录共享的话,就用这个命令结束原来的连接,就可以重新登录 ...

  7. LCD中如何描绘点阵数据

    下载软件“液晶汉字点阵zimo21” 描绘数据 打开软件后,新建图像-取模方式选择C51(A51和C51区别就是,A-F开头要加0,例如0x0AF)-模拟动画中放大格点-描绘图像-点阵生成区 对获得数 ...

  8. javascript实现自动添加文本框功能

    转自:http://www.cnblogs.com/damonlan/archive/2011/08/03/2126046.html 昨天,我们公司的网络小组决定为公司做一个内部的网站,主要是为员工比 ...

  9. 【AnjularJS系列2 】— 表单控件功能相关指令

    第二篇,表单控件功能相关指令. ng-checked控制radio和checkbox的选中状态 ng-selected控制下拉框的选中状态 ng-disabled控制失效状态 ng-multiple控 ...

  10. 《Unix环境高级编程》读书笔记 第13章-守护进程

    1. 引言 守护进程是生存期长的一种进程.它们常常在系统引导装入时启动,仅在系统关闭时才终止.它们没有控制终端,在后台运行. 本章说明守护进程结构.如何编写守护进程程序.守护进程如何报告出错情况. 2 ...