通过改变方块的高度来寻找从源到目的地的最小路径

时间:2012-05-19 20:26:22

标签: algorithm path shortest-path

我们有NXM网格。网格的一个方格是源,一个是目的地。包含源和目标的每个网格都有一些高程(值为0-9的整数)。 我们必须找到从源到目的地的最低成本路径,以满足以下限制:

  1. 路径必须是连续的,即仅在相邻的正方形之间(不是对角线邻接)。
  2. 只能从较高的海拔升至较低的海拔。
  3. 任何方格的高程都可以增加或减少。高程变化的数量计为成本。如果高程没有变化,则将其视为成本。 因此,从源到目的地的路径的总成本是路径中的正方形的高程的变化。此外,源的高程不能改变,但目的地的高程可以

    我尝试应用像Djikstra和APSP这样的算法但无法达到任何解决方案。请帮我解决这个问题。

3 个答案:

答案 0 :(得分:1)

实际上你可能会认为目的地方格的高度也不能改变,因为不需要提升它。

现在,在经典的Dijkstra(类似)算法中,您可以说网格的每个方格都有价格,您可以在此处找到此方块。也就是说,您的源方块的价格 = 0,然后在一个循环中,您将获取下一个最便宜的方块并尝试从它移动到所有相邻的方块,其价格为大。

在你的问题中你有一个额外的自由度:你的方格的高度。也就是说,当你移动到一个正方形时,你可以改变它的高度。

最直接的“蛮力”解决方案如下:

  1. 检查所有细胞,建立一组高程水平(即 所有高度的光谱)。假设你有 H 不同的高度。
  2. 将方块的状态定义为其位置高度。用Dijkstra图来定义你的问题。
  3. 对于每个方块,在图表中添加一个顶点,表示其实际高程。然后添加额外的顶点,用更高的高程表示它(除了源和目标方块)。因此,每个方格都有 H 顶点。
  4. 将每个顶点的内部价格定义为高于其实际位置的高度。因此,对于每个正方形,您都有一个内部价格 = 0(实际高程)的顶点,以及表示具有适当内部价格的高架位置的顶点数
  5. 通过定向边缘连接表示邻居方块的顶点。仅当源顶点的高程至少为目标顶点时才放置边。
  6. 然后通过Dijkstra(或A *)算法找到最短路径。移动的成本被视为目标顶点的内部价格(我们的边缘不带价格)。

    简单来说,我们已经构建了一个“分层”图形,每个图层对应一个可选的高度。在每个位置,您可以在当前层进行移动,也可以降低移动速度。

    不用说问题复杂性增加了。顶点计数增加了因子(最多) H ,同样适用于边数。

    路径搜索基本上具有log(N) * N * M的复杂性,其中 N 是正方形单元格数, M - 是连接顺序(连接最近的数量)邻居)。通货膨胀后,复杂性增加(高达) H ^ 2 因子。

    这样算法的效率取决于不同高度的数量。如果你的高度很低 - 算法应该是有效的。但是,如果你的所有方块都有不同的高度 - 也许应该采用另一种方法。

答案 1 :(得分:1)

这是另一个维度的简单最短距离问题的例子,尝试制定这样的问题, cost [n] [m] [max_height] = {INFINITY};

cost [srcX] [srcY] [height [srcX] [srcY]] = 0;

现在成本[x + 1] [y] [ht] = min(成本[x + 1] [y] [ht],成本[x] [y] [q_ht] +(q_ht - ht)) q_ht从max_height到ht不等。该想法是以任何可允许的高度(即高度> = ht)以最低成本达到(x + 1,y,ht)。这又一次我们需要计算所有ht(0到max_height)。完整的实施在这里: -

#define HIGHVAL 100000000
#define XI (x + a[i])
#define YI (y + b[i])

int n,m;

bool isvalid(int x,int y)
{
    return (x>=0 && y>=0 && x<n && y<m);
}

int main()
{

    int pondX, pondY;
    int farmX, farmY;

    cin>>n>>m>>pondX>>pondY>>farmX>>farmY;
    pondX--, pondY--, farmX--, farmY--;

    int height[n][m];
    string s;

    for(int i=0; i<n; i++)
    {
        cin>>s;
        for(int j=0; j<m; j++)
            height[i][j] = (int)(s[j] - '0');
    }

    int ht = height[pondX][pondY];
    int cost[n][m][ht+1];
    bool visited[n][m];

    for(int i=0; i<n; i++)
        for(int j=0; j<m; j++)
            for(int k=0; k<ht+1; k++)
                cost[i][j][k] = HIGHVAL;

    cost[pondX][pondY][ht] = 0;
    int a[4]= {0,0,1,-1};
    int b[4]= {1,-1,0,0};

    int ci = pondX, cj = pondY;
    queue<int> qx;
    queue<int> qy;

    visited[pondX][pondY] = 1;

    memset(visited, 0, sizeof(visited));
    qx.push(pondX);
    qy.push(pondY);

    while(qy.size())
    {
        int x = qx.front();
        int y = qy.front();

        qx.pop();
        qy.pop();

        for(int i=0; i<4; i++)
        {
            int temp = 0;
            if(isvalid(XI, YI))
            {
                if(!visited[XI][YI])
                {
                    qx.push(XI);
                    qy.push(YI);
                    visited[XI][YI] = 1;
                    temp = 1;
                }

                for(int j=ht; j>=0; j--)
                {
                    int q = HIGHVAL;
                    for(int k=ht; k>=j; k--)
                    {
                        q = min(q, cost[x][y][k] + abs(j - height[XI][YI]));
                    }

                    if(cost[XI][YI][j] > q)
                    {
                        cost[XI][YI][j] = q;

                        if(visited[XI][YI] && !temp)
                        {
                            qx.push(XI);
                            qy.push(YI);
                        }
                    }
                }
            }

        }
    }

    int ans=HIGHVAL;
    for(int i=0; i<=ht; i++)
        ans = min(ans, cost[farmX][farmY][i]);

    cout<<ans;
    //cout<<" "<<n<<m;
    return 0;

}

答案 2 :(得分:0)

“更改高程”出现首先使问题复杂化,但如果你仔细考虑一下,那就不是了。首先,没有理由减少平方的高程 - 仅增加。并且没有任何理由提升任何广场,而是搜索当前“分支”的“结束”广场。 AND ,在任何遍历中,没有任何理由将方块的高度增加到超出可以移动到下一个方格所需的量。我可能会补充说,最佳路径永远不会循环回来。因此,在搜索的每个步骤中,应该提升的正方形以及提升它的数量是完全确定的。您不需要使用搜索来找到它。所以@valdo提出的“分层图”实际上并不是必需的。

凭借这种洞察力,您几乎可以直接使用任何标准路径寻找算法。