【问题标题】:Finding minimum total length of line segments to connect 2N points找到连接 2N 个点的线段的最小总长度
【发布时间】:2020-04-28 06:52:25
【问题描述】:

我正在尝试通过蛮力解决这个问题,但是当给定 7(即 2*7 点)时,它似乎运行得很慢。

注意:我只需要将它运行到最大 2*8 点

问题陈述:

给定二维平面中的 2*N 个点,将它们成对连接起来形成 N 个线段。最小化所有线段的总长度。

例子:

输入:5 10 10 20 10 5 5 1 1 120 3 6 6 50 60 3 24 6 9 0 0

输出:118.4

#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <iomanip>
using namespace std;

class point{
public:
    double x, y;
};

double getLength(point a, point b){
    return hypot((a.x - b.x), (a.y - b.y));
}

static double mini = INT_MAX;

void solve(vector <point> vec, double sum){
    double prevSum = sum;
    if(sum > mini){
        return;
    }
    if(vec.size() == 2){
        sum += getLength(vec[0], vec[1]);
        mini = min(mini, sum);
        return;
    }
    for(int i = 0; i < vec.size() - 1; i++){
        for(int j = i + 1; j < vec.size(); j++){
            sum = prevSum;
            vector <point> temp = vec;
            sum += getLength(temp[i], temp[j]);
            temp.erase(temp.begin() + j);
            temp.erase(temp.begin() + i);
            solve(temp, sum);
        }
    }
}

int main(){
    point temp;
    int input;
    double sum = 0;
    cin >> input;
    vector<point> vec;
    for(int i = 0; i < 2 * input; i++){
        cin >> temp.x >> temp.y;
        vec.push_back(temp);
    }
    solve(vec, sum);
    cout << fixed << setprecision(2) << mini << endl;
}

如何加快这段代码的速度?

【问题讨论】:

  • 您正在使用递归 - 您是否尝试过以非递归方式对其进行编码?
  • 我预计大部分开销都在temp.erase() 中,它可能大部分时间都花在相当于memmove() 上。
  • 你真的需要一个分析器来查看热点在哪里
  • 除了复制和擦除问题:mini的初始值很大,然后用测试if (sum &gt; mini)初步排除明显不良解决方案的过程效率不高。例如,首先尝试更好地估计它,例如通过首先尝试一个贪心算法来初始化这个值mini
  • @CássioRenan std::hypot 是数字库的一部分。一个不方便的using namespace std

标签: c++ algorithm performance optimization brute-force


【解决方案1】:

我认为这不是您要寻找的东西,但为了完整起见,我还是提到它。该问题可以表述为混合整数规划 (MIP) 问题。

我们有距离:

d(i,j) = distance between point i and j (only needed for i<j)

和决策变量

x(i,j) = 1 if points i and j are connected (only needed for i<j)
         0 otherwise

那么我们可以这样写:

可以使用广泛可用的 MIP 求解器来解决此问题,并得出经过验证的最佳解决方案。一个50分的小例子:

【讨论】:

    【解决方案2】:

    您可以通过使用 next_permutation() 逐个遍历所有排列来迭代地解决此问题。为混乱的代码道歉,但这应该告诉你如何去做:

    struct Point {
    
        Point(int x, int y) : x(x), y(y) {
        }
    
    
        bool operator< (const Point& rhs) {
            const int key1 = y * 1000 + x;
            const int key2 = rhs.y * 1000 + rhs.x;
            return  key1 < key2;
        }
    
        double dist(const Point& next) {
    
            const double h = (double)(next.x - x);
            const double v = (double)(next.y - y);
            return sqrt(h*h + v*v);
        }
    
        int x, y;
    
    };
    

    您需要运算符,以便为您的点提供某种排序键,因此 next_permutation 可以按字典顺序递增的顺序遍历它们。 双 getShortestDist(std::vector p) {

        double min = 200000;
    
        std::sort(p.begin(), p.end());
    
        while(std::next_permutation(p.begin(), p.end())) {
    
            double sum = 0.0;
            for (int i = 0; i < p.size(); i+= 2) {
                sum += p[i].dist(p[i+1]);
            }
            if (sum < min) {
                min = sum;
            }
        }
    
        return min;
    
    
    }
    
    
    int main(int argc, char*argv[]) {
    
        static const int arr[] = {
            10, 10, 20, 10, 5, 5, 1, 1, 120, 3, 6, 6, 50, 60, 3, 24, 6, 9, 0, 0
        };
        std::vector<Point> test;
    
        for (int i = 0; i < 20; i += 2) {
            test.push_back(Point(arr[i], arr[i+1]));
            printf("%d %d\n", arr[i], arr[i+1]);
        }
    
        printf("Output: %d, %f", test.size(), getShortestDist(test));
    }
    

    【讨论】:

    • 在尝试这个之后,我认为生成每个排列而不是当前的递归解决方案需要更长的时间? pastebin.com/31azNY81
    • 我的错,你是对的。这更像是一个组合问题,不需要所有排列。我正在再次查看您的代码,我认为您的算法是合理的。我认为矢量复制和擦除可能有问题,所以我尝试对其进行了一些优化。下面的代码几乎完成了您的代码正在做的事情,除了它交换到向量的末尾而不是复制和擦除:pastebin.com/vkfhNjC1
    • ^ 我忘了距离优化。这是更好的链接:pastebin.com/xYyr5D9S
    猜你喜欢
    • 1970-01-01
    • 2012-01-16
    • 1970-01-01
    • 2011-01-16
    • 1970-01-01
    • 1970-01-01
    • 2017-08-29
    • 2021-09-27
    • 1970-01-01
    相关资源
    最近更新 更多