Как лучше хранить кратчайший путь и его длину в лабиринте? - PullRequest
0 голосов
/ 14 апреля 2020

Я использую алгоритм Ли, чтобы найти кратчайший путь в лабиринте.

Лабиринт состоит из 0 и 1. Ячейка со значением 1 представляет собой пустое пространство, к которому можно go, ячейка со значением 0 - это препятствие, которое нельзя пройти.

Однако, выполняя часть q.pop() внутри while(!q.isEmpty()) l oop, я теряю свои «узлы» (я реализовал структуру pozitie_plan, в которой хранится индекс строки, индекс столбца и расстояние между произвольно выбранной точкой в ​​матрице и самой собой). Эти «узлы» могут быть частью пути, который я хочу вывести в конце. Тем не менее, я думаю, что я не должен изменять эту часть.

Я ограничен в использовании очереди в DLLA (Dynami c Linked List on Array). Я реализовал свой собственный. При необходимости я также добавлю его реализацию.

Способ, которым я нахожу путь, заключается в определении наименьшей длины с использованием алгоритма Ли и оставлении следа на вспомогательной матрице, значение в (i, j ) ячейка, представляющая расстояние от источника до этой ячейки.

Наконец, я отслеживаю путь (может быть более одного, я полагаю), начиная с выхода из лабиринта (при условии, что выход был найдено) в начальную позицию путем поиска тех ячеек, которые имеют значение, равное: значение в текущей ячейке - 1.

Я держу эти pozitie_plan узлы в очереди, затем возвращаю очередь в конце и вывод окончательного результата.

Я чувствую, что это может быть сделано лучше. Но как?

Вот мой код. Если потребуется, я могу отредактировать сообщение и изменить опубликованный код на версию, имеющую несколько cout с и более комментариев и / или реализацию для Очереди.

main. cpp

#include <iostream>
#include <vector>
#include "Queue.h"
#include "ShortTest.h"
#include "ExtendedTest.h"

#define Nmax 25

using std::cin;
using std::cout;
using std::vector;
using std::swap;

#include <fstream>
using std::ifstream;
using std::ofstream;


template<typename T>
void citeste_mat(ifstream &input_stream, T mat[][Nmax], int &n, int &m)
{
    input_stream >> n >> m; 
    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
            input_stream >> mat[i][j];
}

void citeste_poz_start(ifstream &input_stream, pozitie_plan &p)
{
    input_stream >> p.i >> p.j;
    p.dist = 0;
    p.i--; p.j--;               // utilizatorul vede in indexare de la 1, dar eu am implementat in indexare de la 0!
}

template<typename V>
void citeste_date(ifstream &input_file, V mat[][Nmax], int &n, int &m, pozitie_plan &p)
{
    citeste_mat<V>(input_file, mat, n, m);
    citeste_poz_start(input_file, p);
}

template<typename U> // ar fi fost problema daca puneam aici tot "T" in loc de "U" ?
void afis_mat(U mat[][Nmax], int n, int m)
{
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
            cout << mat[i][j] << " ";
        cout << '\n';
    }
    cout << '\n';
}

bool valid_pozitie( int i, int j, int n, int m)
{
    return 0 <= i && i < n && 0 <= j && j < m;
}

template<typename T>
bool obstacol(T mat[][Nmax], pozitie_plan p)
{
    return mat[p.i][p.j] == 0;
}

template<typename T>
bool valid_nevizitat(T aux[][Nmax], pozitie_plan p)
{
    return aux[p.i][p.j] == false;
}

template<typename T, typename U>
bool valid(T mat[][Nmax], U aux[][Nmax], pozitie_plan p, int n, int m)
{
    return valid_pozitie(p.i, p.j, n, m) && !obstacol<T>(mat, p) && valid_nevizitat<U>(aux, p);
}

// e un pic "overhead" sa am toate functiile astea de valid, stiu; dar parca imi place sa am totul cat se poate de fragmentat

bool pe_contur(pozitie_plan p, int n, int m)
{
    // return p.i == 0 || p.j == 0 || p.i == n - 1 || p.j == m - 1;
    return p.i * p.j == 0 || p.i == n-1 || p.j == m-1;
}

template<typename T>
bool solution(T mat[][Nmax], pozitie_plan p , int n, int m)
{
    return !obstacol(mat, p) && pe_contur(p, n, m);
}


template<typename T>
pozitie_plan gaseste_cale(T mat[][Nmax], int n, int m, pozitie_plan ps)
{
    // voi pastra integritatea matricii ( mat nu va fi modificata )

    const int dx[] = { -1, 0, 1, 0 };
    const int dy[] = { 0, 1, 0, -1};

    bool visited[Nmax][Nmax] = { false };

    visited[ps.i][ps.j] = true;             // marchez pozitia de start ca vizitata 

    Queue q;
    q.push(ps);                             // bag in coada pozitia de start

    pozitie_plan current_pos;
    int row, column, distance;

    while (!q.isEmpty())
    // cat timp exista in coada pozitii pentru care trebuie verificati vecinii (daca vecinii sunt iesire sau nu)
    {
        current_pos = q.top();
        if (solution<int>(mat, current_pos, n, m))
        {
            return current_pos; // voi returna prima solutie gasita, care garantat va fi cea mai scurta (BFS)
        }

        q.pop();        // stocand head ul in `current_pos` pot spune ca am tratat ce aveam in head, asa cu fac pop

        for (int k = 0; k < 4; k++)
        {
            row = current_pos.i + dx[k];
            column = current_pos.j + dy[k];
            distance = current_pos.dist + 1;

            pozitie_plan vecin{ row, column, distance };

            if (valid<int,bool>(mat, visited, vecin, n, m))

            {
                mat[row][column] = distance;
                visited[row][column] = true;
                q.push(vecin);
            }
        }
    }

    return NULL_POZITIE_PLAN;
}

void reconstruct_path(int mat[][Nmax], int n, int m, pozitie_plan end, pozitie_plan begin, Queue& q)
{
    const int dx[] = { -1, 0, 1, 0 };
    const int dy[] = { 0, 1, 0, -1 };

    q.push(end);

    pozitie_plan current_pos = end;
    int row, column, distance;
    int len = current_pos.dist;
    while(len != 1)
    {
        for (int k = 0; k < 4; k++)
        {
            row = current_pos.i + dx[k];
            column = current_pos.j + dy[k];
            distance = mat[row][column];
            if (valid_pozitie(row, column, n, m)
                && distance == len - 1)
            {
                pozitie_plan new_pos = pozitie_plan{ row, column, distance };
                q.push(new_pos);
                current_pos = new_pos;
                break;
            }
        }
        len--;
    }
    q.push(begin);
}


void reverse_queue(Queue& q, const int len)
{
    pozitie_plan *aux = new pozitie_plan[len];
    for (int i = 0; i < len; i++)
        aux[i] = q.pop();

    for (int i = 0; i < len / 2; i++)
        swap(aux[i], aux[len - 1 - i]);

    for (int i = 0; i <len ; i++)
        q.push(aux[i]);
}


int main()
{

    int mat[Nmax][Nmax] = { 0 };
    int n, m;
    pozitie_plan pozitie_start;

    ifstream input_file;
    input_file.open("input.txt", ios::in);
    citeste_date<int>(input_file, mat, n, m, pozitie_start);        
    input_file.close();

    afis_mat<int>(mat, n, m);

    // pana aici citesc datele de intrare

    pozitie_plan end = gaseste_cale<int>(mat, n, m, pozitie_start); // presupun ca utilizatorul numara liniile & coloanele de la 1
    if (end == NULL_POZITIE_PLAN) 
    {
        cout << "NO SOLUTION FOUND!\n";
    }
    else
    {
        // Queue cale = reconstruct_path(mat, n, m, end);
        Queue cale;
        reconstruct_path(mat, n, m, end, pozitie_start, cale);
        reverse_queue(cale, end.dist + 1);  
        cout << "The shortest path (length = " << end.dist << " not including the starting position) to be followed in the given matrix (above) is:\n";
        cale.toString();
    }
    return 0;
}

Пример

input.txt

18 20
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 1
0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
17 3

консольный выход

The shortest path...// everything is ok, the path is ok
...