文本输入的最短路径算法

时间:2013-09-15 06:42:30

标签: c++ algorithm graph-algorithm

我一直在尝试做这个最短路径的问题,我意识到我尝试它的方式几乎完全错误,我不知道完成它。

问题要求您在给定输入文本文件的情况下找到从一个点到另一个点的最短路径。

The input looks like this with the first value representing how many levels there are.

4  
14 10 15  
13 5 22  
13 7 11  
5

This would result in an answer of: 14+5+13+11+5=48

Which represents a graph like this

问题是要求从左下角到右上角的最短路径。

我尝试这样做的方法是比较任一路径的值,然后将它们添加到总和中。例如,我提供的输入的第一步将比较14与10 + 15.我遇到的问题是,如果两个值相同,它将填补其余的工作。

我希望这是有道理的。

对于使用的算法或任何示例代码的任何建议都将非常感激。

4 个答案:

答案 0 :(得分:1)

使用深度优先搜索并仅添加最小值。然后检查哪一侧是最短的楼梯。如果是图形问题,请查看有向图。对于每个楼梯,您需要2个顶点。从阶梯到阶梯的成本可能是其他原因。

答案 1 :(得分:1)

假设您的数据文件被读入以下格式的二维数组:

int weights[3][HEIGHT] = {
  {14, 10, 15},
  {13, 5, 22},
  {13, 7, 11},
  {X, 5, X}
};

其中X可以是任何东西,无关紧要。为此,我假设权重为正,因此从来没有必要考虑一个“降低”水平的路径。

一般来说,您可以说最低成本是以下2项成本中的较小成本:
1)提升水平的成本:通往对方的路径的成本从1级以下加上即将到来的成本。
2)跨越一个级别的成本:相同级别的相反路径的成本加上遇到的成本。

int MinimumCost(int weight[3][HEIGHT]) {
  int MinCosts[2][HEIGHT]; // MinCosts[0][Level] stores the minimum cost of reaching
                           // the left node of that level
                           // MinCosts[1][Level] stores the minimum cost of reaching
                           // the right node of that level

  MinCosts[0][0] = 0; // cost nothing to get to the start
  MinCosts[0][1] = weight[0][1]; // the cost of moving across the bottom

  for (int level = 1; level < HEIGHT; level++) {
     // cost of coming to left from below right
     int LeftCostOneStep = MinCosts[1][level - 1] + weight[2][level - 1];
     // cost of coming to left from below left then across
     int LeftCostTwoStep = MinCosts[0][level - 1] + weight[0][level - 1] + weight[1][level];
     MinCosts[0][level] = Min(LeftCostOneStep, LeftCostTwoStep);

     // cost of coming to right from below left
     int RightCostOneStep = MinCosts[0][level - 1] + weight[0][level - 1];
     // cost of coming to right from below right then across
     int RightCostTwoStep = MinCosts[1][level - 1] + weight[1][level - 1] + weight[1][level];
     MinCosts[1][level] = Min(RightCostOneStep, RightCostTwoStep);

  }

  return MinCosts[1][HEIGHT - 1];
}

我没有仔细检查语法,请仅使用它来大致了解如何解决问题。您还可以重写算法,以便MinCosts使用常量内存,MinCosts [2] [2],您的整个算法可以成为状态机。

您也可以使用dijkstra算法来解决这个问题,但这有点像用核弹头杀死苍蝇。

答案 2 :(得分:1)

我的第一个想法是用矩阵表示图形,然后运行DFS或Dijkstra来解决它。但对于这个问题,我们可以做得更好。

所以,这是在O(n)中运行的这个问题的可能解决方案。 2*i表示级别i的左侧节点,2*i+1表示级别i的右侧节点。阅读此解决方案中的注释以获得解释。

#include <stdio.h>

struct node {
    int lup; // Cost to go to level up
    int stay; // Cost to stay at this level
    int dist; // Dist to top right node
};


int main() {
    int N;
    scanf("%d", &N);

    struct node tab[2*N];


    // Read input.
    int i;
    for (i = 0; i < N-1; i++) {
        int v1, v2, v3;
        scanf("%d %d %d", &v1, &v2, &v3);
        tab[2*i].lup = v1;
        tab[2*i].stay = tab[2*i+1].stay = v2;
        tab[2*i+1].lup = v3;
    }
    int v;
    scanf("%d", &v);
    tab[2*i].stay = tab[2*i+1].stay = v;


    // Now the solution:

    // The last level is obvious:
    tab[2*i+1].dist = 0;
    tab[2*i].dist = v;

    // Now, for each level, we compute the cost.
    for (i = N - 2; i >= 0; i--) {
        tab[2*i].dist = tab[2*i+3].dist + tab[2*i].lup;
        tab[2*i+1].dist = tab[2*i+2].dist + tab[2*i+1].lup;

        // Can we do better by staying at the same level ?
        if (tab[2*i].dist > tab[2*i+1].dist + tab[2*i].stay) {
            tab[2*i].dist = tab[2*i+1].dist + tab[2*i].stay;
        }
        if (tab[2*i+1].dist > tab[2*i].dist + tab[2*i+1].stay) {
            tab[2*i+1].dist = tab[2*i].dist + tab[2*i+1].stay;
        }
    }

    // Print result
    printf("%d\n", tab[0].dist);

    return 0;
}

(此代码已在给定示例中进行了测试。)

答案 3 :(得分:0)

该算法的简单版本的想法如下:

  • 定义顶点列表(您可以留下的位置)和边缘(您可以做的步行)
  • 每个顶点都有一个连接到其他顶点的边列表
  • 为每个边缘存储行走长度
  • 为每个顶点存储一个1000000000的字段,意思是“步行到这里多久”
  • 创建一个仅使用起点
  • 初始化的“活动”顶点列表
  • 将起始顶点的步行距离字段设置为0(您在这里)

现在搜索算法继续

  1. 从最低walk_distance的“有效列表”中选择(a)顶点并将其从列表中删除
  2. 如果顶点是你完成的目的地。
  3. 否则该顶点中的每条边计算到other_vertex的步行距离为

    new_dist = vertex.walk_distance + edge.length

  4. 检查新距离是否短于other_vertex.walk_distance,并在这种情况下将other_vertex.walk_distance更新为新值,并将该顶点放在“活动列表”中(如果它尚未存在)。 / p>

  5. 从1
  6. 重复

    如果活动列表中的节点用尽且从未处理过目标顶点,则意味着无法从起始顶点到达目标顶点。

    对于C ++中的数据结构,我会使用类似

    的内容
    struct Vertex {
        double walk_distance;
        std::vector<struct Edge *> edges;
        ...
    };
    
    struct Edge {
        double length;
        Vertex *a, *b;
        ...
        void connect(Vertex *va, Vertex *vb) {
            a = va; b = vb;
            va->push_back(this); vb->push_back(this);
        }
        ...
    };
    

    然后从输入中我知道,对于n级别,需要2*n个顶点(每个楼层的左侧和右侧)和2*(n-1) + n个边缘(每个楼梯一个)每个楼层散步一个。)

    对于除最后一层之外的每个楼层,您需要构建三条边,对于最后一层只有一条。

    我还首先在向量中分配所有边和顶点,稍后修复指针(构造后设置是一个反模式,但这里是为了避免重新分配的问题,并且仍然保持非常简单)。

    int n = number_of_levels;
    
    std::vector<Vertex> vertices(2*n);
    std::vector<Edge> edges(2*(n-1) + n);
    
    for (int i=0; i<n-1; i++) {
        Vertex& left = &vertices[i*2];
        Vertex& right = &vertices[i*2 + 1];
        Vertex& next_left = &vertices[(i+1)*2];
        Vertex& next_right = &vertices[(i+1)*2 + 1];
        Edge& dl_ur = &edges[i*3];   // down-left to up-right stair
        Edge& dr_ul = &edges[i*3+1]; // down-right to up-left stair
        Edge& floor = &edges[i*3+2];
    
        dl_ur.connect(left, next_right);
        dr_ul.connect(right, next_left);
        floor.connect(left, right);
    }
    
    // Last floor
    edges.back().connect(&vertex[2*n-2], &vertex[2*n-1]);
    

    注意:未经测试的代码

    修改

    当然,这个算法可以解决一个更普遍的问题,即顶点和边的集合是任意的(但长度是非负的)。

    对于非常具体的问题,可以使用更简单的算法,甚至不需要任何数据结构,而是可以在读取输入时动态计算结果。

    #include <iostream>
    #include <algorithm>
    
    int main(int argc, const char *argv[]) {
        int n; std::cin >> n;
        int l=0, r=1000000000;
        while (--n > 0) {
            int a, b, c; std::cin >> a >> b >> c;
            int L = std::min(r+c, l+b+c);
            int R = std::min(r+b+a, l+a);
            l=L; r=R;
        }
        int b; std::cin >> b;
        std::cout << std::min(r, l+b) << std::endl;
        return 0;
    }
    

    这个解决方案的想法非常简单:

    • l变量是地板左侧的walk_distance
    • r变量是右侧的walk_distance

    算法:

    1. 我们初始化l=0r=1000000000,因为我们在左侧

    2. 对于所有中间步骤,我们读取了三个距离:

      a是从左下到右上楼梯的长度

      b是发言权的长度

      c是从左上方到左上方的长度

    3. 我们计算下一层左侧和右侧的walk_distance

      Lr+cl+b+c之间的最小值(我们从右侧开始,或者我们从左侧开始到那里)

      Rl+ar+b+a之间的最小值(我们从左起,或者我们从右开始,然后首先跨越发言)

    4. 在最后一步,我们只需要选择rl之间的最小值之间的最小值,越过最后一层