POJ - 2728 Desert King
# Desert King
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 32228 Accepted: 8815
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
4
题目大意:第一行给出n代表有n个点,接下来的每一行输入点的x,y,z(高度)坐标。让你将点连起来,使得每个点都至少有一条路可以抵达除自己外任意点。(即构成最小生成树)连接i,j需要i,j高度差的成本,构成\(\sqrt{(i.x - j.x)^2+(i.y - j.y)^2}\)长的路。要求成本总和除以路的总长度最小。
01分数规划 + 二分答案 + 最小生成树
题目要求\(\frac{\sum_{i=0}cost}{\sum_{i = 0}length}\)最小。
假设\(\frac{\sum_{i=0}cost}{\sum_{i = 0}length}\)<x,将公式化成\(\sum_{i=0}(cost - x * length)\)<0。如果成立则说明所求比率即最小比率比x小,如果不成立说明(所求比率)要比x大。然后对x用二分来找。对于每个x都,构建最小生成树,并将每条边的cost - x * length加起来,判断假设是否成立。
一开始用的Kruskal算法构建最小生成树,然后TLE了。。。
然后改用Prim算法来构建。
从网上得知,kruskal适用于点多边少的稀疏图,Prim适用于点少边多的稠密图。
附上代码
#include<stdio.h>
#include<math.h>
#include <stdlib.h>
struct xyz{
int x,y,z;
}a[1001] = {};
struct dh{
double d;
int h;
}b[1001][1001] = {};
int n;
double prim(double x)
{
double cost[1001] = {},minc = 1e17,sum = 0;
int num,p = 1;//把一作为起始点
bool check[1001] = {};
check[1] = true;
for (int i = 2;i<=n;i++) cost[i] = (double)b[p][i].h - x * b[p][i].d; //把1到其他点的距离录入cost数组
for (int q = 1;q<=n - 1;q++)
{
minc = 1e17;
for (int i = 2;i<=n;i++)//寻找最小代价
{
if (cost[i] < minc && check[i] == false)
{
minc = cost[i];
num = i;
}
}
sum = sum + minc;
p = num;
check[p] = true;
for (int i = 2;i<=n;i++)//更新cost数组
{
double t = (double)b[p][i].h - x * b[p][i].d;
if (check[i] == false && t <cost[i]) cost[i] = t;
}
}
return sum;
}
int main()
{
scanf("%d",&n);
while (n != 0)
{
for (int i = 1;i<=n;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
for (int i = 1;i<=n-1;i++)
for (int j = i+1;j<=n;j++)
{
b[i][j].d = sqrt((a[i].x - a[j].x) * (a[i].x - a[j].x) + (a[i].y - a[j].y) * (a[i].y - a[j].y));
b[i][j].h = abs(a[i].z - a[j].z);
b[j][i].d = b[i][j].d;
b[j][i].h = b[i][j].h;
}
double l = 0,r = 10000000,m;
while (r - l>1e-5)//二分答案
{
m = (l + r) / 2;
double t = prim(m);
if (t < 0) r = m;
else l = m;
}
printf("%.3f\n",l);
scanf("%d",&n);
}
}
顺带想问个问题,为什么Prim算法不能一边更新一边寻找最小代价。 好像是可以的,我室友就是这么做出来的,应该是我哪里没注意到orz
本文来自博客园,作者:Un-Defined,转载请保留本文署名Un-Defined,并在文章顶部注明原文链接:https://www.cnblogs.com/EIPsilly/p/15606149.html