题目大意:在二维平面上找出一个点,使它到所有给定点的距离和最小,距离定义为欧氏距离,求这个最小的距离和是多少(结果需要四舍五入)?
思路:如果不能加点,问所有点距离和的最小值那就是经典的MST,如果只可以加一个点问最小值就是广义的费马点的问题,如果加点的数目不加限制,那问题就成了斯坦纳树的问题(介个属于NPC问题)
这题显然就是广义费马点问题,可以采用局部贪心法,从一个初始点出发,不断向上下左右四个方向拓展,如果在一个方向上走过去到所有点的距离和小于目前这个点到所有点的距离和,那就更新目前点的值,直到从四个方向拓展都不能比目前的点更优,那就说明目前的点属于局部最优,那就减少步长,从刚才给出点处继续搜索,直到精度小于题目所需的精度(整数)
需要注意的是局部贪心(又叫爬山法)很容易卡在局部最优上,因此往往随机初始点多次,以保证局部最优是全局最优,但广义费马点好像这种局部最优的点不多的样子,选原点为初始点就可以AC这题
//POJ2420
#include <cstdio>
#include <iostream>
#include <string.h>
#include <math.h>
#define maxn 110
const short dx[5]={0,1,-1,0,0},dy[5]={0,0,0,1,-1};
using namespace std;
double x[maxn]={0},y[maxn]={0};int n;
double sumlen(double xx,double yy)
{
double ret=0;
for(int i=1;i<=n;i++)
{
ret+=sqrt((xx-x[i])*(xx-x[i])+(yy-y[i])*(yy-y[i]));
}
return ret;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lf%lf",&x[i],&y[i]);
double a=0,b=0,min=sumlen(a,b),step=100;
while (step>0.1)
{
short flag=1;
while (flag==1)//如果这层循环结束,说明从四个方向拓展都不能得到更优解也就是达到了局部最优解
{
flag=0;
for(int i=1;i<=4;i++)
{
doublexx=a+dx[i]*step,yy=b+dy[i]*step,t=sumlen(xx,yy);
if (t<min){min=t;a=xx;b=yy;flag=1;}
}
}
step/=2;
}
printf("%d",(int)(min+0.5));
return 0;
}