题意:摘自NOCOW翻译(http://www.nocow.cn/index.php/Translate:USACO/fence3)
描述
农夫约翰已经决定建造电网。他已经把他的农田围成一些奇怪的形状,现在必须找出安放电源的最佳位置。
对于每段电网都必须从电源拉出一条电线。电线可以穿过其他电网或者跨过其他电线。电线能够以任意角度铺设,从电源连接到一段电网的任意一点上(也就是,这段电网的端点上或者在其之间的任意一点上)。这里所说的“一段电网”指的是呈一条线段状的电网,并不是连在一起的几段电网。若几段电网连在一起,那么也要分别给这些电网提供电力。
已知所有的 F(1 <= F <= 150)段电网的位置(电网总是和坐标轴平行,并且端点的坐标总是整数,0 <= X,Y <= 100)。你的程序要计算连接电源和每段电网所需的电线的最小总长度,还有电源的最佳坐标。
电源的最佳坐标可能在农夫约翰的农田中的任何一个位置,并不一定是整数。
[编辑]格式
PROGRAM NAME: fence3
INPUT FORMAT
第一行包括 F ——电网的数量。 下面的 F 行每行包括两个 X,Y 对,表示这段电网的两个端点。
(ps:数据中有电网是点的情况,即 68 97 68 97,这貌似与题目叙述不符,请注意 //from Error)(路人甲:貌似照做就行了,不用管,。。。)
OUTPUT FORMAT
只有一行,输出三个浮点数,每个保留1位小数。假定你的电脑的输出库会正确地对小数进行四舍五入。
这三个数是:
电源最佳坐标的 X 值, 电源最佳坐标的 Y 值,和 需要的电线的总长度(要最小)。
[编辑]SAMPLE INPUT (file fence3.in)
3 0 0 0 1 2 0 2 1 0 3 2 3
[编辑]SAMPLE OUTPUT (file fence3.out)
1.0 1.6 3.7
解题思路:
- 直接0.01精度搜索必然会爆时间,所以需要逐步提高精度
- 首先以step = 10的精度搜索坐标(x, y),找到最小的那些坐标(min_x, min_y),那么全局最小的点的坐标(g_min_x, g_min_y)肯定满足:min_x - step <= g_min_x <= min_x + step,min_y - step <= g_min_y <= min_y + step,也就是以点(min_x, min_y)为中心,2 * step为边长的正方形中
- 下一步就是以step = 1的精度在2中得出的范围中搜索,然后逐步提高精度,一直到step = 0.01
- 求出step = 0.01中距离最小的点即可
代码:
/*
ID: zc.rene1
LANG: C
PROG: fence3
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#define MAX 100
#define MAX_DISTANCE 9999999.0
int F;
double fences[150][4];
double global_min_distance = MAX_DISTANCE, global_min_x, global_min_y;
void GetInput(FILE *fin)
{
int i, j;
fscanf(fin, "%d", &F);
for (i=0; i<F; i++)
{
for (j=0; j<4; j++)
{
fscanf(fin, "%lf", &fences[i][j]);
}
}
}
double DeltaDistance(double x, double y, int index)
{
double x1 = fences[index][0];
double y1 = fences[index][1];
double x2 = fences[index][2];
double y2 = fences[index][3];
double ret = 0.0, temp;
if (x1 > x2)
{
temp = x1;
x1 = x2;
x2 = temp;
}
if (y1 > y2)
{
temp = y1;
y1 = y2;
y2 = temp;
}
if (x < x1)
{
ret += ((x1 - x) * (x1 - x));
}
if (x > x2)
{
ret += ((x - x2) * (x - x2));
}
if (y < y1)
{
ret += ((y1 - y) * (y1 - y));
}
if (y > y2)
{
ret += ((y - y2) * (y - y2));
}
return sqrt(ret);
}
double GetDistance(double x, double y)
{
int i;
double ret = 0.0;
for (i=0; i<F; i++)
{
ret += DeltaDistance(x, y, i);
}
return ret;
}
void CalDistance(double min_x, double max_x, double min_y, double max_y, double step)
{
double x, y, new_step;
double temp_distance, temp_min_distance = MAX_DISTANCE;
double stack[10][2];
int i, bottom = -1, top = -1;
for (x=min_x; x<=max_x; x+=step)
{
for (y=min_y; y<=max_y; y+=step)
{
temp_distance = GetDistance(x, y);
if (temp_distance < temp_min_distance)
{
temp_min_distance = temp_distance;
top = 0;
stack[top][0] = x;
stack[top][1] = y;
}
else if (temp_distance == temp_min_distance)
{
top++;
stack[top][0] = x;
stack[top][1] = y;
}
}
}
new_step = step / 10.0;
if (new_step >= 0.01)
{
while (top > bottom)
{
x = stack[top][0];
y = stack[top][1];
CalDistance(x - step, x + step, y - step, y + step, new_step);
top--;
}
}
else
{
if (temp_min_distance < global_min_distance)
{
for (i=bottom+1; i<=top; i++)
{
global_min_distance = temp_min_distance;
global_min_x = stack[i][0];
global_min_y = stack[i][1];
}
}
}
}
int main(void)
{
FILE *fin, *fout;
fin = fopen("fence3.in", "r");
fout = fopen("fence3.out", "w");
GetInput(fin);
CalDistance(0.0, 100.0, 0.0, 100.0, 10.0);
fprintf(fout, "%.1lf %.1lf %.1lf\n", global_min_x, global_min_y, global_min_distance);
return 0;
}