Desert King
Time Limit: 3000MS |
|
Memory Limit: 65536K |
Total Submissions: 17772 |
|
Accepted: 5011 |
Description
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
Source
题目大意:有n个村庄要连在一起,村与村之间的长度为他们之间的水平距离,连在一起的花费是两村的高度差。现求所花费和与长度和之比最小。
解题思路:最优比率生成树。自然的,被大家狂刷的模板题+经典题。心血来潮看了看0-1分数规划。这题就是0-1分数规划的经典用例。刚研究不久,有点手生,先把自己认识到的东西写下来,以免忘记。
首先0-1分数规划是分数规划的一个特例。它研究的问题是寻找一个L=min(cx/dx),当然在任何条件下,dx不能为0。又转眼一想,那怎么求L=max(cx/dx)呢。
个人想法是求P=min(dx/cx),然后L=1/P,这样就转化过去了。0-1分数规划,x的取值在{0,1},也就是有些用,有些不用。
一开始很脑残得在想,这样的题目怎么写:你看,如果有n个变量,有些变量的x取0,有些变量的x取1,那要把全部变量的分配情况考虑清楚的话,不是要用2^n的复杂度,果断不行啊。不过看完两个例子之后,突然感觉,其实没有那么麻烦。因为,0-1分数规划问题都是转化到了一个函数Q(L)=min{cx-L*dx}去。这个函数很奇葩。一开始我也在想,为什么这个函数用的是min而不是max呢。后来发现,如果用max,这个函数的一些很重要的性质就不能保证了,比方单调性。正因为这个函数,最优比率生成树就简单了一点。
Q(L)=min{cx-L*dx}=min{(c-L*d)x},也就是说,确定了参数L时,只要确定x的分布就能得到Q(L)的值。这个x的分布意味着什么呢?在生成树里面,它意味着这条边取或不取。那Q(L)的意义是,当一个图的边权是C-L*d的时候的最小生成树。这样一来,开心了。最小生成树的没问题。还有,Q(L)随L的增大而递减,当Q(L)=0的时候,L就是所要求的值。这样一来,二分就完全没有问题了。一般还是迭代法用的比较多。首先让L取一个比较大的值,这个值确定不会是解,然后,如果算出来Q(L)==0,那不用说,就跳出了,如果Q(L)!=0,就让L等于当前的cx/dx。迭代法速度比二分快很多。
再举个不是最优比率生成树的0-1规划问题:某个图里面,每条边都有权值w,求把这个图分成s和t两个部分的边割集中,平均边权最小的那个。首先一想:有点想网络流,其次,边割集,应该和最小割多少有点关系。这样思考:每条边都有选和不选两种情况,对应x的{0,1}取值,它要求的不就是L=min{wx/dx},wx是权值和,dx是边数和,求最小平均。于是建立函数Q(L)=min{wx-L*dx}=min{(w-L*d)x},x表示在不在这个边割集中,Q(L)表示一个图的权值为w-L*d的最小割。到这里一切都OK了。Isap用上,迭代法用上,AC想必是时间的问题。目前,我认识的0-1分数规划就这么多了
最优比例生成树详解:http://hi.baidu.com/zzningxp/item/28aa46e0fd86bdc2bbf37d03
相关证明:http://www.cnblogs.com/scau20110726/archive/2012/10/19/2730896.html
题意:
每条路径有一个 cost 和 dist,求图中 sigma(cost) / sigma(dist) 最小的生成树。
思路:
1. sigma(cost)/sigma(dist) <= r 有 cost - r * dist <= 0
2. r = 0 当然是理想的初始状态,我们不断尝试 r 使结果不断逼近最优值,方能得到结果。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int VM=1010;
const double INF=1e15;
const double eps=1e-5;
struct node{
double x,y,z;
}e[VM];
double dist[VM][VM],cost[VM][VM],map[VM][VM],dis[VM];
int path[VM],n,vis[VM];
double Prim(double ratio){
int i,j;
for(i=1;i<=n;i++){
map[i][i]=0;
for(j=i+1;j<=n;j++)
map[i][j]=map[j][i]=cost[i][j]-ratio*dist[i][j];
}
for(i=1;i<=n;i++){
dis[i]=map[1][i];
vis[i]=0;
path[i]=1;
}
dis[1]=0;
vis[1]=1;
int k;
for(i=1;i<n;i++){
double tmp=INF;
for(j=1;j<=n;j++)
if(!vis[j] && tmp>dis[j]){
k=j;
tmp=dis[j];
}
vis[k]=1;
for(j=1;j<=n;j++)
if(!vis[j] && dis[j]>map[k][j]){
dis[j]=map[k][j];
path[j]=k;
}
}
double d=0,c=0;
for(i=1;i<=n;i++){
d+=dist[i][path[i]];
c+=cost[i][path[i]];
}
return c/d;
}
int main(){
//freopen("input.txt","r",stdin);
while(~scanf("%d",&n) && n){
for(int i=1;i<=n;i++)
scanf("%lf%lf%lf",&e[i].x,&e[i].y,&e[i].z);
for(int i=1;i<=n;i++){
dist[i][i]=cost[i][i]=0;
for(int j=i+1;j<=n;j++){
dist[i][j]=dist[j][i]=sqrt((e[i].x-e[j].x)*(e[i].x-e[j].x)+(e[i].y-e[j].y)*(e[i].y-e[j].y));
cost[i][j]=cost[j][i]=fabs(e[i].z-e[j].z);
}
}
double r1=0,r2=0;
while(1){
r2=Prim(r1);
if(fabs(r1-r2)<=eps)
break;
r1=r2;
}
printf("%.3f\n",r1);
}
return 0;
}