Enmascaramiento de bits y programación dinámica | Conjunto-2 (TSP)

En esta publicación, utilizaremos nuestro conocimiento de la programación dinámica y la técnica de enmascaramiento de bits para resolver uno de los famosos problemas NP-hard «Problema del vendedor ambulante».
Antes de resolver el problema, asumimos que el lector tiene el conocimiento de 
 

Para entender este concepto, consideremos el siguiente problema: 
Descripción del problema : 
 

Given a 2D grid of characters representing 
a town where '*' represents the 
houses, '#' represents the blockage, 
'.' represents the vacant street 
area. Currently you are (0, 0) position.

Our task is to determine the minimum distance 
to be moved to visit all the houses and return
to our initial position at (0, 0). You can 
only move to adjacent cells that share exactly
1 edge with the current cell.

El problema anterior es el conocido problema del viajante de comercio.
La primera parte es calcular la distancia mínima entre las dos celdas. Podemos hacerlo simplemente usando un BFS ya que todas las distancias son unidades de distancia. Para optimizar nuestra solución, calcularemos previamente las distancias tomando la ubicación inicial y la ubicación de las casas como el punto de origen de nuestro BFS.
Cada recorrido BFS toma tiempo O (tamaño de la cuadrícula). Por lo tanto, es O(X * size_of_grid) para el cálculo previo general, donde X = número de casas + 1 (posición inicial)
Ahora pensemos en un estado de DP 
Entonces necesitaremos rastrear las casas visitadas y la última casa visitada para identificar unívocamente un estado en este problema.
Por lo tanto, tomaremos dp[index][mask]como nuestro estado DP. 
 

Aquí, 
index : nos dice la ubicación de la casa actual
mask : nos dice las casas que se visitan (si el i-ésimo bit está configurado en mask, significa que se limpia el i-ésimo mosaico sucio) 
 

Mientras que dp[index][mask] nos dirá la distancia mínima para visitar X (número de bits establecidos en la máscara) casas correspondientes a su orden de aparición en la máscara donde la última casa visitada es la casa en el índice de ubicación.
Relación de transición de estado
Entonces, nuestro estado inicial será dp[0][0], esto indica que actualmente estamos en el mosaico inicial, que es nuestra ubicación inicial, y la máscara es 0, lo que indica que no se ha visitado ninguna casa hasta ahora.
Y nuestro estado de destino final será dp[any index][LIMIT_MASK] , aquí LIMIT_MASK = (1<<N) – 1 
y N = número de casas.
Por lo tanto, nuestra transición de estado DP se puede establecer como 
 

dp(curr_idx)(curr_mask) = min{
    for idx : off_bits_in_curr_mask
       dp(idx)(cur_mask.set_bit(idx)) + dist[curr_idx][idx]
}

La relación anterior se puede visualizar como la distancia mínima para visitar todas las casas al pararse en la casa curr_idx y ya visitando las casas cur_mask es igual a la distancia mínima entre la casa curr_idx y la casa idx + la distancia mínima para visitar todas las casas al pararse en idx house y ya 
visitando ( cur_mask | (1 <<idx) ) casas.
Entonces, aquí iteramos sobre todos los valores posibles de idx, de modo que cur_mask tenga el i -ésimo bit como 0 que nos dice que la i -ésima casa no se visita.
Siempre que tengamos nuestra máscara = LIMIT_MASK, esto significa que hemos visitado todas las casas del pueblo. Entonces, agregaremos la distancia desde la última ciudad visitada (es decir, la ciudad en la posición cur_idx) a la posición inicial (0, 0).
El programa C++ para la implementación anterior se proporciona a continuación:
 

C++

#include <bits/stdc++.h>
using namespace std;
 
#define INF 99999999
#define MAXR 12
#define MAXC 12
#define MAXMASK 2048
#define MAXHOUSE 12
 
// stores distance taking source
// as every dirty tile
int dist[MAXR][MAXC][MAXHOUSE];
 
// memoization for dp states
int dp[MAXHOUSE][MAXMASK];
 
// stores coordinates for
// dirty tiles
vector < pair < int, int > > dirty;
 
// Directions
int X[] = {-1, 0, 0, 1};
int Y[] = {0, 1, -1, 0};
 
char arr[21][21];
 
// len : number of dirty tiles + 1
// limit : 2 ^ len -1
// r, c : number of rows and columns
int len, limit, r, c;
 
 
// Returns true if current position
// is safe to visit
// else returns false
// Time Complexity : O(1)
bool safe(int x, int y)
{
    if (x >= r or y>= c or x<0 or y<0)
       return false;
    if (arr[x][y] == '#')
       return false;
    return true;
}
 
 
// runs BFS traversal at tile idx
// calculates distance to every cell
// in the grid
// Time Complexity : O(r*c)
void getDist(int idx){
 
    // visited array to track visited cells
    bool vis[21][21];
    memset(vis, false, sizeof(vis));
 
    // getting current position
    int cx = dirty[idx].first;
    int cy = dirty[idx].second;
 
    // initializing queue for bfs
    queue < pair < int, int > > pq;
    pq.push({cx, cy});
 
    // initializing the dist to max
    // because some cells cannot be visited
    // by taking source cell as idx
    for (int i = 0;i<= r;i++)
        for (int j = 0;j<= c;j++)
            dist[i][j][idx] = INF;
 
    // base conditions
    vis[cx][cy] = true;
    dist[cx][cy][idx] = 0;
 
    while (! pq.empty())
    {
        auto x = pq.front();
        pq.pop();
        for (int i = 0;i<4;i++)
        {
           cx = x.first + X[i];
           cy = x.second + Y[i];
           if (safe(cx, cy))
           {
               if (vis[cx][cy])
                   continue;
               vis[cx][cy] = true;
               dist[cx][cy][idx] = dist[x.first][x.second][idx] + 1;
               pq.push({cx, cy});
            }
         }
    }
}
 
// Dynamic Programming state transition recursion
// with memoization. Time Complexity: O(n*n*2 ^ n)
int solve(int idx, int mask)
{
    // goal state
    if (mask == limit)
       return dist[0][0][idx];
 
    // if already visited state
    if (dp[idx][mask] != -1)
       return dp[idx][mask];
 
    int ret = INT_MAX;
 
    // state transition relation
    for (int i = 0;i<len;i++)
    {
        if ((mask & (1<<i)) == 0)
        {
            int newMask = mask | (1<<i);
            ret = min( ret, solve(i, newMask)
                + dist[dirty[i].first][dirty[i].second][idx]);
        }
    }
 
    // adding memoization and returning
    return dp[idx][mask] = ret;
}
 
void init()
{
    // initializing containers
    memset(dp, -1, sizeof(dp));
    dirty.clear();
 
    // populating dirty tile positions
    for (int i = 0;i<r;i++)
        for (int j = 0;j<c;j++)
        {
            if (arr[i][j] == '*')
               dirty.push_back({i, j});
        }
 
    // inserting ronot's location at the
    // beginning of the dirty tile
    dirty.insert(dirty.begin(), {0, 0});
 
    len = dirty.size();
 
    // calculating LIMIT_MASK
    limit = (1<<len) - 1;
 
    // precalculating distances from all
    // dirty tiles to each cell in the grid
    for (int i = 0;i<len;i++)
       getDist(i);
}
 
int main(int argc, char const *argv[])
{
    // Test case #1:
    //     .....*.
    //     ...#...
    //     .*.#.*.
    //     .......
 
char A[4][7] = {    {'.', '.', '.', '.', '.', '*', '.'},
                    {'.', '.', '.', '#', '.', '.', '.'},
                    {'.', '*', '.', '#', '.', '*', '.'},
                    {'.', '.', '.', '.', '.', '.', '.'}
                };
 
    r = 4; c = 7;
 
    cout << "The given grid : " << endl;
 
    for (int i = 0;i<r;i++)
    {
        for (int j = 0;j<c;j++)
        {
            cout << A[i][j] << " ";
            arr[i][j] = A[i][j];
        }
        cout << endl;
    }
 
    // - initialization
    // - precalculations
    init();
 
    int ans = solve(0, 1);
 
    cout << "Minimum distance for the given grid : ";
    cout << ans << endl;
 
 
    // Test Case #2
    //     ...#...
    //     ...#.*.
    //     ...#...
    //     .*.#.*.
    //     ...#...
 
    char Arr[5][7] = {  {'.', '.', '.', '#', '.', '.', '.'},
                        {'.', '.', '.', '#', '.', '*', '.'},
                        {'.', '.', '.', '#', '.', '.', '.'},
                        {'.', '*', '.', '#', '.', '*', '.'},
                        {'.', '.', '.', '#', '.', '.', '.'}
                };
 
    r = 5; c = 7;
 
    cout << "The given grid : " << endl;
 
    for (int i = 0;i<r;i++)
    {
        for (int j = 0;j<c;j++)
        {
            cout << Arr[i][j] << " ";
            arr[i][j] = Arr[i][j];
        }
        cout << endl;
    }
 
    // - initialization
    // - precalculations
    init();
    ans = solve(0, 1);
    cout << "Minimum distance for the given grid : ";
    if (ans >= INF)
        cout << "not possible" << endl;
    else
        cout << ans << endl;
 
    return 0;
}

Producción: 
 

The given grid : 
. . . . . * . 
. . . # . . . 
. * . # . * . 
. . . . . . . 
Minimum distance for the given grid : 16
The given grid : 
. . . # . . . 
. . . # . * . 
. . . # . . . 
. * . # . * . 
. . . # . . . 
Minimum distance for the given grid : not possible

Nota :
hemos utilizado el estado inicial para que sea dp[0][1] porque hemos empujado la ubicación de inicio en la primera posición en el contenedor de casas. Por lo tanto, nuestra máscara de bits será 1 ya que se establece el bit 0 , es decir, hemos visitado la ubicación de inicio de nuestro viaje.
Complejidad temporal :
Considere que el número de casas es n . Entonces, hay n * (2 n ) estados y en cada estado, estamos recorriendo n casas para pasar al siguiente estado y, debido a la memorización, estamos haciendo esta transición de bucle solo una vez para cada estado. Por lo tanto, nuestra Complejidad de Tiempo es O(n 2 * 2 n ) .
Recomendado
 

Este artículo es una contribución de Nitish Kumar . Si te gusta GeeksforGeeks y te gustaría contribuir, también puedes escribir un artículo usando write.geeksforgeeks.org o enviar tu artículo por correo a review-team@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks.
Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.
 

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *