C++ 曼哈顿度量中的最小距离

C++ 曼哈顿度量中的最小距离,c++,algorithm,optimization,C++,Algorithm,Optimization,我试图找到(x,y)中的最小距离。我正在搜索有关这方面的信息。但我什么也没找到 #include<bits/stdc++.h> using namespace std; #define st first #define nd second pair<int, int> pointsA[1000001]; pair<int, int> pointsB[1000001]; int main() { int n, t; unsigned long

我试图找到(x,y)中的最小距离。我正在搜索有关这方面的信息。但我什么也没找到

#include<bits/stdc++.h>
using namespace std;
#define st first
#define nd second

pair<int, int> pointsA[1000001];
pair<int, int> pointsB[1000001];

int main() {
    int n, t;
    unsigned long long dist;

    scanf("%d", &t);

    while(t-->0) {
        dist = 4000000000LL;
        scanf("%d", &n);

        for(int i = 0; i < n; i++) {
            scanf("%d%d", &pointsA[i].st, &pointsA[i].nd);
        }

        for(int i = 0; i < n; i++) {
            scanf("%d%d", &pointsB[i].st, &pointsB[i].nd);
        }

        for(int i = 0; i < n ;i++) {
            for(int j = 0; j < n ; j++) {
                if(abs(pointsA[i].st - pointsB[j].st) + abs(pointsA[i].nd - pointsB[j].nd) < dist) {
                    dist = abs(pointsA[i].st - pointsB[j].st) + abs(pointsA[i].nd - pointsB[j].nd);
                }
            }
            printf("%lld\n", dist);
        }
    }
}
输出:

5
4
我的解决方案(请注意,为简单起见,我不关心曼哈顿区的溢出问题,因此它不适用于无符号长字符:

#包括
#包括
#包括
#包括
#包括
#包括
typedef std::对点;
typedef std::向量点列表;
静态内联布尔cmp_x(常数点a、常数点b)
{
如果(a.首先b.first){
返回false;
}否则{
返回a.secondb秒){
返回false;
}否则{
返回a.firstfirst,&it->second)!=2){
std::abort();
}
断言(它->秒>0);
}
点列表点SB(N);
for(PointsList::iterator it=pointsB.begin(),endi=pointsB.end();it!=endi;++it){
如果(扫描(“%d%d”,&it->first,&it->second)!=2){
std::abort();
}
断言(它->秒<0);
}
排序(pointsA.begin()、pointsA.end()、cmp_by_y);
排序(pointsB.begin()、pointsB.end()、cmp_by_y);
常量点列表::常量迭代器min_a_by_y=pointsA.begin();
常量点列表::常量迭代器max_b_by_y=(pointsB.rbegin()+1).base();
断言(*max_b_by_y==pointsB.back());
无符号区=曼哈顿区(*min_a_by_y,*max_b_by_y);
const unsigned diff_x=std::abs(minu a_by_y->first-max_b_by_y->first);
常数无符号最佳_diff_y=dist-diff_x;
const int max_y_for_a=max_b_by_y->秒+距离;
const int min_y_for_b=min_a_by_y->second-dist;
PointsList::迭代器;
对于(it=pointsA.begin()+1;it!=pointsA.end()&&it->second second>=min\u y\u for_b;+rit){
}
if(rit!=pointsB.rend()){
pointsB.erase(pointsB.begin(),(rit+1.base());
}
std::sort(pointsA.begin()、pointsA.end()、cmp_by_x);
std::sort(pointsB.begin()、pointsB.end()、cmp_by_x);
对于(size_t j=0;diff_x>0&&j=cur_a_point.second);
const int diff_x=距离-最佳差异;
const int min_x=当前点。第一个-差值x+1;
const int max_x=当前第一个点+diff_x-1;
常量点搜索项=std::make_pair(max_x,std::numeric_limits::min());
PointsList::const\u迭代器可能在\u it=std::下限附近(pointsB.begin()、pointsB.end()、搜索项、cmp\u by\u x);
for(PointsList::const\u reverse\u迭代器rit(可能在它附近);rit!=pointsB.rend()&&rit->first>=min\ux;++rit){
const unsigned cur_dist=曼哈顿区(cur_a_point,*rit);
如果(当前距离<距离){
dist=cur_dist;
}
}
}
printf(“%u\n”,dist);
}
}
我的机器上的基准测试(Linux+i7 2.70 GHz+gcc-Ofast-march=native):

$make bench
时间。/test1test1\u res
实数0m7.846s
用户0m7.820s
系统0m0.000s
时间./test2test2\u res
实际0.605s
用户0.590秒
sys 0m0.010s

test1
是您的变体,
test2
是我的变体。

您需要学习如何编写函数以及如何使用容器。以您当前的编码风格,不可能得到更好的解决方案

问题是更好的解决方案是递归方法。按X坐标对点进行排序。现在递归地将集合一分为二,并确定每一半内的最近距离以及两个点之间的最近距离

最后一部分是有效的,因为两半都是按X排序的。将左半部分的最后一个值与右半部分的第一个值进行比较,可以得到一个很好的距离上限。

因此,您可以进行一个非常简单的优化,可以节省大量时间

由于您声明集合A中的所有点的y>0,集合B中的所有点的y<0,您可以立即放弃A中y>Mindsist的所有点和B中y<-Mindsist的所有点。这些点永远不能比当前最近的一对更近:

for(int i = 0; i < n ;i++) {
    if (pointsA[i].nd > dist)
        continue; // <- this is the big one
    for(int j = 0; j < n ; j++) {
        if (pointsB[j].nd < -dist)
            continue; // <- helps although not as much
        if(abs(pointsA[i].st - pointsB[j].st) + abs(pointsA[i].nd - pointsB[j].nd) < dist) {
            dist = abs(pointsA[i].st - pointsB[j].st) + abs(pointsA[i].nd - pointsB[j].nd);
        }
    }
    printf("%lld\n", dist);
}
for(int i=0;idist)

继续;//在a组和B组中保留一个候选列表,最初包含整个输入。取y中最接近的一对,取a的最小y和最大y B,计算曼哈顿距离,并从候选列表中删除y大于上限的任何候选列表。这可能会删除输入,或者可能基本上没有影响,但实际上是无效的和廉价的第一步

现在,在x和y中对剩余的候选项进行排序。这将在y中为您提供一个分离列表,在x中为一个混合列表,即O(N log N),其中N已被削减,希望但不一定是第一步。 对于每个点,现在计算其在y(平凡)中的最近邻点和在x中的最近邻点
#include <cstdlib>
#include <cstdio>
#include <cassert>
#include <vector>
#include <limits>
#include <algorithm>

typedef std::pair<int, int> Point;
typedef std::vector<std::pair<int, int> > PointsList;

static inline bool cmp_by_x(const Point &a, const Point &b)
{
    if (a.first < b.first) {
        return true;
    } else if (a.first > b.first) {
        return false;
    } else {
        return a.second < b.second;
    }
}

static inline bool cmp_by_y(const Point &a, const Point &b)
{
    if (a.second < b.second) {
        return true;
    } else if (a.second > b.second) {
        return false;
    } else {
        return a.first < b.first;
    }
}

static inline unsigned manhattan_dist(const Point &a, const Point &b)
{
    return std::abs(a.first - b.first) +
        std::abs(a.second - b.second);
}

int main()
{
    unsigned int n_iter = 0;
    if (scanf("%u", &n_iter) != 1) {
        std::abort();
    }
    for (unsigned i = 0; i < n_iter; ++i) {
        unsigned int N = 0;
        if (scanf("%u", &N) != 1) {
            std::abort();
        }
        if (N == 0) {
            continue;
        }
        PointsList pointsA(N);
        for (PointsList::iterator it = pointsA.begin(), endi = pointsA.end(); it != endi; ++it) {
            if (scanf("%d%d", &it->first, &it->second) != 2) {
                std::abort();
            }
            assert(it->second > 0);
        }
        PointsList pointsB(N);
        for (PointsList::iterator it = pointsB.begin(), endi = pointsB.end(); it != endi; ++it) {
            if (scanf("%d%d", &it->first, &it->second) != 2) {
                std::abort();
            }
            assert(it->second < 0);
        }

        std::sort(pointsA.begin(), pointsA.end(), cmp_by_y);
        std::sort(pointsB.begin(), pointsB.end(), cmp_by_y);
        const PointsList::const_iterator min_a_by_y = pointsA.begin();
        const PointsList::const_iterator max_b_by_y = (pointsB.rbegin() + 1).base();
        assert(*max_b_by_y == pointsB.back());

        unsigned dist = manhattan_dist(*min_a_by_y, *max_b_by_y);
        const unsigned diff_x = std::abs(min_a_by_y->first - max_b_by_y->first);
        const unsigned best_diff_y = dist - diff_x;

        const int max_y_for_a = max_b_by_y->second + dist;
        const int min_y_for_b = min_a_by_y->second - dist;
        PointsList::iterator it;
        for (it = pointsA.begin() + 1; it != pointsA.end() && it->second <= max_y_for_a; ++it) {
        }
        if (it != pointsA.end()) {
            pointsA.erase(it, pointsA.end());
        }

        PointsList::reverse_iterator rit;
        for (rit = pointsB.rbegin() + 1; rit != pointsB.rend() && rit->second >= min_y_for_b; ++rit) {
        }
        if (rit != pointsB.rend()) {
            pointsB.erase(pointsB.begin(), (rit + 1).base());
        }
        std::sort(pointsA.begin(), pointsA.end(), cmp_by_x);
        std::sort(pointsB.begin(), pointsB.end(), cmp_by_x);

        for (size_t j = 0; diff_x > 0 && j < pointsA.size(); ++j) {
            const Point &cur_a_point = pointsA[j];
            assert(max_y_for_a >= cur_a_point.second);
            const int diff_x = dist - best_diff_y;
            const int min_x = cur_a_point.first - diff_x + 1;
            const int max_x = cur_a_point.first + diff_x - 1;

            const Point search_term = std::make_pair(max_x, std::numeric_limits<int>::min());
            PointsList::const_iterator may_be_near_it = std::lower_bound(pointsB.begin(), pointsB.end(), search_term, cmp_by_x);

            for (PointsList::const_reverse_iterator rit(may_be_near_it); rit != pointsB.rend() && rit->first >= min_x; ++rit) {
                const unsigned cur_dist = manhattan_dist(cur_a_point, *rit);
                if (cur_dist < dist) {
                    dist = cur_dist;
                }
            }
        }
        printf("%u\n", dist);
    }
}
$ make bench
time ./test1 < data.txt  > test1_res

real    0m7.846s
user    0m7.820s
sys     0m0.000s
time ./test2 < data.txt  > test2_res

real    0m0.605s
user    0m0.590s
sys     0m0.010s
for(int i = 0; i < n ;i++) {
    if (pointsA[i].nd > dist)
        continue; // <- this is the big one
    for(int j = 0; j < n ; j++) {
        if (pointsB[j].nd < -dist)
            continue; // <- helps although not as much
        if(abs(pointsA[i].st - pointsB[j].st) + abs(pointsA[i].nd - pointsB[j].nd) < dist) {
            dist = abs(pointsA[i].st - pointsB[j].st) + abs(pointsA[i].nd - pointsB[j].nd);
        }
    }
    printf("%lld\n", dist);
}