题目描述:平面上给你n个点,让你求一个点,到这n点的距离和最小。
解题报告:
先写好一个判定函数double
judge(pint id),表示id这个点的权值,这里的话,权值就是id到其他所有点的距离和。
随即在给定的范围内生成NUM个点,挑选一个最大的步长T(要求答案点距离随即生成的点小于T)。
下面就开始循环了:
1:对于每一个T,扫描第i个随即生成的点。
2:以这个点为中心,上下左右为T的方框内,随机生成TIM个点(模拟这个点偏移T的距离)。
3:把i点更新为这TIM点中权值最优的。
4:T *= deta,缩小,如果小于给定的MIN跳出,否则继续从1开始。
5:最后从NUM点中选取最优的即可。
代码如下:
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<cmath>
#include<cstdio>
using
namespace std;
#define
NUM 20 //随机生成点的个数
#define
TIM 20 //每个点移动的次数
#define RD
1000 //随即乘生成0~1的,精度为1/RD的小数
#define
MIN 0.1 //步长T的最小值,比最后坐标要求的精度小一些
#define
deta 0.7 //每次循环步长T的缩小程度
int n;
//总点数,复杂度X * NUM * TIM * n; X为T到MIN的循环次数,由deta决定
struct
pint
{
double x, y, val;
pint(){}
pint(double x, double y):x(x),y(y){}
}jeo[200],
tar[NUM];
double
dist(pint a, pint b)
{return
sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y -
b.y));}
//判定函数
double
judge(pint id)
{
double sum = 0;
for(int i = 0; i < n; i++)
sum += dist(id, jeo[i]);
return sum;
}
//获取0~1的小数
double
GetDouble()
{return
(rand() % (RD + 1)) * 1.0 / RD;}
//获取左下角为a,右上角为b内的随机点
pint
GetRand(pint a, pint b)
{
pint tar = pint(a.x + (b.x - a.x) * GetDouble(), a.y + (b.y - a.y)
* GetDouble());
tar.val = judge(tar);
return tar;
}
//模拟退火主过程
//MAXT为步长
void
jeogia(double MaxT)
{
for(double T = MaxT; T >= MIN; T *= deta)
for(int i = 0; i < NUM; i++) for(int j = 0; j
< TIM; j++)
{
pint tmp = GetRand(pint(tar[i].x - T, tar[i].y - T), pint(tar[i].x
+ T, tar[i].y + T));
if (tmp.val < tar[i].val) tar[i] = tmp;
}
}
int
main()
{
while(scanf("%d", &n) != EOF)
{
pint a = pint(100000, 100000), b = pint(-100000,
-100000);
for(int i = 0; i < n; i++)
{
scanf("%lf%lf", &jeo[i].x,
&jeo[i].y);
a.x = min(a.x, jeo[i].x); a.y = min(a.y, jeo[i].y);
b.x = max(b.x, jeo[i].x); b.y = max(b.y, jeo[i].y);
}
//随机生成NUM个点
for(int i = 0; i < NUM; i++) tar[i] = GetRand(a,
b);
//传入步长
jeogia(max(b.y - a.y, b.x - a.x));
double ans = -1;
for(int i = 0; i < NUM; i++)
if (ans < 0 || tar[i].val <
ans)
ans = tar[i].val;
printf("%.0f\n", ans);
}
return 0;
}
加载中,请稍候......