关于网易内推编程题堆棋子的解法

第一题堆棋子,当场对角懵逼、拉格朗日懵逼、。。。然后就自然而然地跪了= =。心态很重要,心态炸了一切都完了。
后来跟室友讨论了一下,算是完整的找到了一个解法,然后上来翻帖子发现精品贴已经贴出来了,思路完全一样,但是缺少说明和证明,所以在这里补充一下。
首先得证明这么一个问题,当给定i个棋子的坐标后,最后全部汇聚到一个格子,使总移动步数最少,则这个格子的横坐标应该是所有i个棋子的横坐标的中位数,纵坐标应该是所有i个棋子的纵坐标的中位数(但是要注意该点不一定是i个点之一,这个比较容易被忽略)。室友提出来的,当时我就震惊了,膜拜一下室友。具体证明起来我表达能力较差,直接拋一个博客链接好了:http://blog.csdn.net/zhang20072844/article/details/13372753
曼哈顿距离有意思的地方就在于求和的时候可以把x和y解耦,然后分别考虑。退化到一维后,把上面链接中带权中位数的各权值设为1即可。文中给出的证明我没看(太长不看。。。),其实一维的问题画图很容易证明,给个思路:考虑第一个数和最后一个数的距离、第二个数和倒数第二个数的距离、...。
好了,上面的问题解决后,剩下的就相对简单了。不过有的人可能会想,既然给定i个点后汇聚点的坐标求法知道了,但是一共有n个点啊,选出i个点出来也相当麻烦呢,这里给出的回答是不用选。由于不管i取何值,以及怎么选出这i个点,最后的汇聚点的横坐标一定是n个点的横坐标之一,纵坐标一定是n个点的纵坐标之一,所以只需考虑这n*n种情况就可以了。这其实帮助我们把搜索目标从n个点的矩形包络的量级缩减到了n*n的量级。
最后,设一个有n*n行,n列的矩阵res,每一行存储n*n种汇聚点之一到n个点的曼哈顿距离,然后对每一行从小到大排序,然后逐行依次把第i列赋值为前i-1列数值之和。这时候的矩阵所存储的,其实是对于每一种汇聚点(对应每一行),把离其最近的i个点汇聚过来所需要的总步数。所以,最后的最后,对矩阵逐列求最小值,即为答案。
下面贴出C++的代码,变量命名一塌糊涂,见谅。。。(有一个优化策略,就是n个点的所有横坐标(以及纵坐标,道理一样)中,可能有重复值,这时只用选取一次即可,类似于求一个unique,但是对STL不太熟,就没做= =)
#include <iostream>
#include <algorithm>
using namespace std;

int main(){
    int n;
    cin>>n;
    int x[n];
    int y[n];
    for(int i=0;i<n;i++)
        cin>>x[i];
    for(int i=0;i<n;i++)
        cin>>y[i];
    int res[n*n][n];
    //计算n个点到n*n个候选目标点的距离
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            for(int k=0;k<n;k++){
                res[i*n+j][k] = abs(x[i]-x[k])+abs(y[j]-y[k]);
            }
    //对每个候选目标点到输入的n个点的距离排序(即对res的每一行排序)
    for(int i=0;i<n*n;i++)
        sort(*res+i*n,*res+(i+1)*n);
    //res的第i列,赋值为前i-1列之和,即距其最近的i个点移到该目标点的总移动次数
    for(int i=0;i<n;i++){
        if(i==0)
            continue;
        for(int j=0;j<n*n;j++){
            res[j][i]+=res[j][i-1];
        }
    }
    //求res每一列的最小值,即为答案
    for(int i=0;i<n;i++)
        for(int j=0;j<n*n-1;j++)
            res[0][i] = min(res[0][i],res[j+1][i]);
    for(int i=0;i<n;i++)
        i==0?cout<<res[0][i]:cout<<' '<<res[0][i];
    return 0;
}

#网易##算法工程师#
全部评论
赞一个
点赞 回复 分享
发布于 2017-08-13 14:34
棒棒!
点赞 回复 分享
发布于 2017-08-13 14:37
很明白!赞
点赞 回复 分享
发布于 2017-08-13 14:53
有个地方不明白,就是给出的博客链接上一下说n是奇数的时候取最中间两个值的平均数,一下又说取n/(2+1)
点赞 回复 分享
发布于 2017-08-13 15:33
厉害了,总是以为接近了答案  总是不对 现在终于明白了
点赞 回复 分享
发布于 2017-08-13 16:08
给赞  终于做出来了。
点赞 回复 分享
发布于 2017-08-13 16:54
点赞 回复 分享
发布于 2017-08-13 23:49
6666666666
点赞 回复 分享
发布于 2017-08-13 23:53

相关推荐

拉丁是我干掉的:把上海理工大学改成北京理工大学。成功率增加200%
点赞 评论 收藏
分享
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
11-27 10:46
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务