51nod3148 松鼠聚会

3148 松鼠聚会

有N个小松鼠,它们的家用一个点x,y表示,两个点的距离定义为:点(x,y)和它周围的8个点即上下左右四个点和对角的四个点,距离为1。现在N个松鼠要走到一个松鼠家去,求走过的最短距离和。

输入

第一行给出数字N,表示有多少只小松鼠。0<=N<=10^5
下面N行,每行给出x,y表示其家的坐标。
-10^9<=x,y<=10^9

输出

表示为了聚会走的路程和最小为多少

输入样例

输入样例1:
6
-4 -1
-1 -2
2 -4
0 2
0 3
5 -2
输入样例2:
6
0 0
2 0
-5 -2
2 -2
-1 2
4 0

输出样例

输出样例1:
20
输出样例2:
15

解析:

很明显的单峰函数,可以使用爬山解决。

本题算法流程:

1、 初始化球心为各个给定点的重心(即其各维坐标均为所有给定点对应维度坐标的平均值),以减少枚举量。 设定一个爬山算法的温度初始值,开始进行下面的循环过程。 

2、 对于当前的球心,求出每个已知点到这个球心欧氏距离的平均值。

3、 遍历所有已知点。记录一个改变值 cans(分开每一维度记录)对于每一个点的欧氏距离,如果大于平均值,就把改变值加上差值,否则减去。实际上并不用判断这个大小问题,只要不考虑绝对值,直接用坐标计算即可。这个过程可以形象地转化成一个新的球心,在空间里推来推去,碰到太远的点就往点的方向拉一点,碰到太近的点就往点的反方向推一点。

4、 将我们记录的 cans 乘上温度,更新球心,回到步骤 2

5、 在温度小于某个给定阈值的时候结束。

因此,我们在更新球心的时候,不能直接加上改变值,而是要加上改变值与温度的乘积。

并不是每一道爬山题都可以具体地用温度解决,这只是一个例子。

放代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#define N 100005
#define inf 1000000000
#define ll long long
struct node{int x,y;}a[N];
int n,mn=inf,mx=-inf,bx[N],by[N];ll ans=inf*10000000ll,sumx[N],sumy[N];
using namespace std;
int main()
{
    scanf("%d",&n);int x,y;
    for(int i=1;i<=n;i++)
    {
       scanf("%d%d",&x,&y);
       a[i].x=x+y,a[i].y=x-y;
       bx[i]=a[i].x;by[i]=a[i].y;
    }
    sort(bx+1,bx+n+1);
    sort(by+1,by+n+1);
    for(int i=1;i<=n;i++)
       sumx[i]=sumx[i-1]+bx[i],sumy[i]=sumy[i-1]+by[i];
    for(int i=1;i<=n;i++)
    {
        ll h=0;ll l;
        l=lower_bound(bx+1,bx+n+1,a[i].x)-bx;
        h+=(l*2-n)*a[i].x*1ll-sumx[l]*2+sumx[n];
        l=lower_bound(by+1,by+n+1,a[i].y)-by;
        h+=(l*2-n)*a[i].y*1ll-sumy[l]*2+sumy[n];
        if(h<ans)ans=h;
    }
    printf("%lld\n",ans/2);
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值