Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding minimum total length of line segments to connect 2N points

I'm trying to solve this problem by bruteforce, but it seems to run very slow when given 7 (which is 2*7 points).

Note: I only need to run it to maximum 2*8 points

Problem statement:

Given 2*N points in a 2d plane, connect them in pairs to form N line segments. Minimize the total length of all the line segments.

Example:

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

Output: 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;
}

How can I speed up this code ?

like image 441
Andrean Lay Avatar asked Jan 10 '20 15:01

Andrean Lay


2 Answers

I don't think this is what you are looking for but I mention it for completeness sake anyway. The problem can be formulated as a Mixed Integer Programming (MIP) problem.

We have distances:

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

and decision variables

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

Then we can write:

enter image description here

Solving this problem can be done with widely available MIP solvers and leads to proven optimal solutions. A small example with 50 points:

enter image description here

like image 115
Erwin Kalvelagen Avatar answered Nov 18 '22 12:11

Erwin Kalvelagen


You can solve this iteratively by using next_permutation() to go through all the permutations one by one. Apologies for the messy code, but this should show you how to do it:

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;

};

You need the operator so you have some sort of sorting key for your points, so next_permutation can go through them in lexicographical increasing order. double 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));
}
like image 37
civet-elle Avatar answered Nov 18 '22 13:11

civet-elle