寻找最小必要体积的气体容器

时间:2015-08-09 11:09:14

标签: c++ algorithm brute-force

我在比赛的某个地方发现了这个问题,而且还没有能够提出解决方案。

  

N个城市的坐标为(x,y)。我必须从头开始   城市到达第二个城市。每个城市都有一个加油站。   所以我必须找到最小量的气体容器才能到达   最后的城市。   例如:

Input:
3
17 4
19 4
18 5
Output:
1.414

在这里,我的方式是:1->3->2

我使用简单的强力方法,但速度很慢。如何优化我的代码? 也许有更好的解决方案?

#include <iostream>
#include <algorithm>
#include <stack>
#include <math.h>
#include <cstring>
#include <iomanip>
#include <map>
#include <queue>
#include <fstream>
using namespace std;

int n, used[203];

double min_dist;

struct pc {
    int x, y;
};

pc a[202];

double find_dist(pc a, pc b) {
    double dist = sqrt( (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y) );
    return dist;
}

void functio(double d, int used[], int k, int step) {
    used[k] = 1;
    if(k == 1) {
        if(d < min_dist) {
            min_dist = d;
        }
        used[k] = 0;
        return;
    }

    for(int i = 1; i < n; ++i) {
        if(i != k && used[i] == 0) {
            double temp = find_dist(a[k], a[i]);

            if(temp > d) {
                if(temp < min_dist)
                functio(temp, used, i, step + 1);
            }
            else {
                if(d < min_dist)
                functio(d, used, i, step + 1);
            }
        }
    }
    used[k] = 0;
}

int main() {

    cin >> n;

    for(int i = 0; i < n; ++i)
        cin >> a[i].x >> a[i].y;

    min_dist = 1000000;
    memset(used, 0, sizeof(used));
    functio(0, used, 0, 0);
    cout << fixed  << setprecision(3) << min_dist << endl;
}

3 个答案:

答案 0 :(得分:3)

最小生成树具有编码顶点之间所有路径的整洁属性,可最小化路径上最长边的长度。对于Euclidean MST,您可以计算Delaunay三角剖分,然后运行您最喜欢的O(m log n)时间算法(在m = O(n)边的图上),总运行时间为O(n log n)。或者,您可以使用具有良好常量的O(n ^ 2)-time算法运行具有天真优先级队列的Prim(特别是如果您利用SIMD)。

答案 1 :(得分:1)

因此,您在算法中尝试优化的是您在两个城市之间旅行的最长距离。因为那是你的油箱需要的大小。 这是最短路径的变体,因为您正在尝试优化enire路径长度。

我认为你可以逃脱这个:

  • 制作边列表。 (每对城市之间的距离)

  • 从列表中删除最长边,除非这会导致目标无法访问。

  • 一旦您无法移除最长的路径,这意味着这是您前往目的地的限制因素。其余的路线不再重要了。

然后最后你应该有一个边缘列表,它构成了源和目的地之间的路径。

我没有证明这个解决方案是最优的,所以没有保证。但请考虑一下:如果你删除最长的路径,只有较短的路径,所以最大的腿距不会增加。

关于复杂性,时间复杂度为O(n log n),因为您必须对边缘进行排序 内存复杂度为O(n ^ 2)

这可能不是最有效的算法,因为它是一种图算法,并没有使用城市在欧几里德平面上的事实。那里可能有一些优化......

答案 2 :(得分:1)

您可以使用二进制搜索将时间复杂度降低到O(n^2*log(n)),这将在1秒的时间限制内运行。二元搜索背后的想法是,如果我们可以使用x卷从城市1到达城市2,则无需检查更高容量的容器。如果我们无法使用此功能,那么我们需要超过x音量。要检查我们是否可以使用x卷访问城市2,您可以使用BFS。如果两个城市之间的距离相距x,那么它可以从一个城市移动到另一个城市,我们可以说它们是通过边缘连接的。

代码:

int vis[203];
double eps=1e-8;

struct pc {
    double x, y;
};

double find_dist(pc &a, pc &b) {
    double dist=sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
    return dist;
}

bool can(vector<pc> &v, double x) {  // can we reach 2nd city with volume x
    int n=v.size();
    vector<vector<int>> graph(n, vector<int>(n, 0));  // graph in adjacency matrix form
    // set edges in graph
    for(int i=0; i<n; i++) {
        for(int j=0; j<n; j++) {
            if(i==j) continue;  //same city
            double d=find_dist(v[i], v[j]);
            if(d<=x) graph[i][j]=1; // can reach from city i to city j using x volume
        }
    }


    // perform BFS
    memset(vis, 0, sizeof(vis));
    queue<int> q;
    q.push(0); // we start from city 0 (0 absed index)
    vis[0]=1;
    while(!q.empty()) {
        int top=q.front();
        q.pop();
        if(top==1) return true; // can reach city 2 (1 in 0-based index)
        for(int i=0; i<n; i++) {
            if(top!=i && !vis[i] && graph[top][i]==1) {
                q.push(i);
                vis[i]=1;
            }
        }
    }
    return false; // can't reach city 2
}


double calc(vector<pc> &v) {  // calculates minimum volume using binary search
    double lo=0, hi=1e18;
    while(abs(hi-lo)>eps) {
        double mid=(lo+hi)/2;
        if(can(v, mid)) {
            hi=mid;   // we need at most x volume
        } else{
            lo=mid;  // we need more than x volumer
        }
    }
    return lo;
}