棋盘移动最短路径问题(Dijkstra算法)

Problem

给定一个矩阵A[m][n] ,矩阵上各点的值为移动到该点的代价,每次可以从该点的对角线或直线方向移动一格,求从点A[0][0]移动到点A[m-1][n-1]的最小代价。
比如矩阵,求从左上角的1移动到右下角的1的最小代价。
1 3 0 0 1 2
1 2 0 0 2 3
1 1 1 0 0 1
1 1 1 2 1 1
3 0 0 1 0 1

solution

本题可以看成是带权重的有向图上单源最短路径问题,将棋盘上的点看成是一组节点集合。每一个点至多与其他八个点直接相连,,即点 u 发出的边至多为8,A[m][n]表示移动到该点的权重。因此可以用Dijkstra算法求解。

Dijkstra算法维持的一组关键信息是一组节点集合S,该集合中的每个点到源节点 s 的最短路径已经被找到。算法重复的从节点结合 VS 中选择最短路径最小的的节点 u ,将u加入到集合S,然后对所有从u发出的边进行松弛。

Dis(G,w,s)
Inital_Single_Source(G,s)
s=空集
Q=G.V
while(Q!= 空集)
    u=Extract_min(Q);
    S.push(u)
    for each vertex v in G.Adj[u]
        Relax(u,v,w)
Inital_Single_Source(G,s)//初始化
    for each v(v!=s) in G
        v.dis = 无穷大
        v.pre = NIL
    //设初始点
    s.dis = 0;
    s.pre = -1;
Relax(u,v,w)
    if(v.dis > u.dis + w(u,v))
        v.dis > u.dis + w(u,v)
        v.pre = u

Source code

#include<iostream>
#include<stack>
using namespace std;
const int Max = 10000;

struct Node
{
    int dist;
    int pre;
};

void Init(Node* gNode, int count)
{
    for (int i = 0; i < count; i++)
    {
        gNode[i].dist = Max;
        gNode[i].pre = -1;
    }
}
void Relax(Node* gNode, int** map, int u, int v, int node)
{
    if (gNode[v].dist > gNode[u].dist + map[v / node][v % node])
    {
        gNode[v].dist = gNode[u].dist + map[v / node][v % node];
        gNode[v].pre = u;
    }
}
int main(void)
{
    freopen("test.in", "r", stdin);
    freopen("result.out", "w", stdout);
    int hang,lie,  i, j;
    cin >> hang >> lie;//hang:行数,lie:列数

    //存原始棋盘
    int **map = new int*[lie];
    for (i = 0; i < hang; i++)
        map[i] = new int[lie];


    for ( i = 0; i < hang; i++)
    for (j = 0; j < lie; j++)
        cin >> map[i][j];

    for (i = 0; i < hang; i++)
    {
        for (j = 0; j < lie; j++)
            cout << map[i][j]<<" ";
        cout << endl;
    }

    int count = hang*lie;
    Node * gNode = new Node[count];
    int *S = new int[count];//集合S,保存被找到最短路径的结点置1,否则置0;集合Q为集合S的补集
    //S[i]置为空
    for (i = 0; i < count; i++)
        S[i] = 0;

    //Initial
    Init(gNode, count);
    gNode[0].dist = 0;
    gNode[0].pre = -1;
    int flag = 0;//检测Q是否为空
    while (flag < count)//循环节点个数次
    {
        //find min
        int min = Max;
        int index = -1;
        for (i = 0; i < count; i++)
        {
            if (S[i]==0 && gNode[i].dist < min )//S[i]未被加入集合中
            {
                min = gNode[i].dist;
                index = i;
            }
        }
        S[index] = 1;
        flag++;
        int col = index / lie;
        int row = index % lie;
        //对每个节点发出的每条边做松弛操作
        if ((col - 1) >= 0)
        {
            Relax(gNode, map, index, index - lie, lie);
            if ((row - 1) >= 0)
                Relax(gNode, map, index, index - lie-1, lie);
            if ((row+1)<lie)
                Relax(gNode, map, index, index - lie+1, lie);
        }
        if (col >= 0 && col < hang)
        {
            if ((row - 1) >= 0)
                Relax(gNode, map, index, index - 1, lie);
            if ((row + 1)<lie)
                Relax(gNode, map, index, index+ 1, lie);
        }
        if ((col + 1) < hang)
        {
            Relax(gNode, map, index, index + lie, lie);
            if ((row - 1) >= 0)
                Relax(gNode, map, index, index + lie - 1, lie);
            if ((row + 1)<lie)
                Relax(gNode, map, index, index + lie + 1, lie);
        }

    }
    int cost = gNode[count - 1].dist + map[0][0];//因为初始点也有权重
    cout << "Min cost is " << cost << endl;
    int path = count-1;
    stack<int> stackPath;

    while (path != 0)
    {
        stackPath.push(path);
        path = gNode[path].pre;
    }
    stackPath.push(0);
    while (!stackPath.empty())
    {
        cout << stackPath.top()<< " ";
        stackPath.pop();
    }

}

输入实例:
5 6
1 3 0 0 1 2
1 2 0 0 2 3
1 1 1 0 0 1
1 1 1 2 1 1
3 0 0 1 0 1

输出:
1 3 0 0 1 2
1 2 0 0 2 3
1 1 1 0 0 1
1 1 1 2 1 1
3 0 0 1 0 1
Min cost is 5
0 7 8 15 22 29 (行优先存储)

    原文作者:Dijkstra算法
    原文地址: https://blog.csdn.net/lime1991/article/details/47847123
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞