Open In App

Bitmasking and Dynamic Programming | Travelling Salesman Problem

Improve
Improve
Improve
Like Article
Like
Save Article
Save
Share
Report issue
Report

In this post, we will be using our knowledge of dynamic programming and Bitmasking technique to solve one of the famous NP-hard problem “Traveling Salesman Problem”.
Before solving the problem, we assume that the reader has the knowledge of 
 

To understand this concept lets consider the below problem : 
Problem Description
 

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.

The above problem is the well-known Travelling Salesman Problem.
The first part is to calculate the minimum distance between the two cells. We can do it by simply using a BFS as all the distances are unit distance. To optimize our solution we will be pre-calculating the distances taking the initial location and the location of the houses as the source point for our BFS.
Each BFS traversal takes O(size of grid) time. Therefore, it is O(X * size_of_grid) for overall pre-calculation, where X = number of houses + 1 (initial position)
Now let’s think of a DP state 
So we will be needing to track the visited houses and the last visited house to uniquely identify a state in this problem.
Therefore, we will be taking dp[index][mask] as our DP state. 
 

Here, 
index : tells us the location of current house
mask : tells us the houses that are visited ( if ith bit is set in mask then this means that the ith dirty tile is cleaned) 
 

Whereas dp[index][mask] will tell us the minimum distance to visit X(number of set bits in mask) houses corresponding to their order of their occurrence in the mask where the last visited house is house at location index.
State transition relation
So our initial state will be dp[0][0] this tells that we are currently at initial tile that is our initial location and mask is 0 that states that no house is visited till now.
And our final destination state will be dp[any index][LIMIT_MASK], here LIMIT_MASK = (1<<N) – 1 
and N = number of houses.
Therefore our DP state transition can be stated as 
 

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

The above relation can be visualized as the minimum distance to visit all the houses by standing at curr_idx house and by already visiting cur_mask houses is equal to min of distance between the curr_idx house and idx house + minimum distance to visit all the houses by standing at idx house and by already 
visiting ( cur_mask | (1 <<idx) ) houses.
So, here we iterate over all possible idx values such that cur_mask has ith bit as 0 that tells us that ith house is not visited.
Whenever we have our mask = LIMIT_MASK, this means that we have visited all the houses in the town. So, we will add the distance from the last visited town (i.e the town at cur_idx position) to the initial position (0, 0).
The C++ program for the above implementation is given below:
 

 

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

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;
}


Java




/*package whatever //do not write package name here */
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
 
class pair {
    int first;
    int second;
    pair(int first, int second)
    {
        this.first = first;
        this.second = second;
    }
}
class GFG {
    static int INF = 99999999;
    static int MAXR = 12;
    static int MAXC = 12;
    static int MAXMASK = 2048;
    static int MAXHOUSE = 12;
 
    // stores distance taking source
    // as every dirty tile
    static int dist[][][] = new int[MAXR][MAXC][MAXHOUSE];
 
    // memoization for dp states
    static int dp[][] = new int[MAXHOUSE][MAXMASK];
    // stores coordinates for
    // dirty tiles
    static ArrayList<pair> dirty = new ArrayList<>();
    // Direction
    static int X[] = { -1, 0, 0, 1 };
    static int Y[] = { 0, 1, -1, 0 };
 
    static char arr[][] = new char[21][21];
 
    // len : number of dirty tiles + 1
    // limit : 2 ^ len -1
    // r, c : number of rows and columns
    static int len, limit, r, c;
 
    // Returns true if current position
    // is safe to visit
    // else returns false
    // Time Complexity : O(1)
    public static boolean safe(int x, int y)
    {
        if (x >= r || y >= c || x < 0 || 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)
    public static void getDist(int idx)
    {
        // visited array to track visited cells
        boolean vis[][] = new boolean[21][21];
 
        // getting current position
        int cx = dirty.get(idx).first;
        int cy = dirty.get(idx).second;
 
        // initializing queue for bfs
        Queue<pair> pq = new LinkedList<>();
        pq.add(new pair(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 condition
        vis[cx][cy] = true;
        dist[cx][cy][idx] = 0;
 
        while (!pq.isEmpty()) {
            pair x = pq.peek();
            pq.poll();
            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.add(new pair(cx, cy));
                }
            }
        }
    }
    // Dynamic Programming state transition recursion
    // with memoization. Time Complexity: O(n*n*2 ^ n)
    public static 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 = Integer.MAX_VALUE;
 
        // state transition rlation
        for (int i = 0; i < len; i++) {
            if ((mask & (1 << i)) == 0) {
                int newMask = mask | (1 << i);
                ret = Math.min(
                    ret,
                    solve(i, newMask)
                        + dist[dirty.get(i).first]
                              [dirty.get(i).second][idx]);
            }
        }
 
        // adding memoization and returning
        return dp[idx][mask] = ret;
    }
    public static void init()
    {
        // initializing containers
        for (int x[] : dp)
            Arrays.fill(x, -1);
 
        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.add(new pair(i, j));
 
        // inserting ronot's location at the
        // beginning of the dirty tile
        dirty.add(0, new pair(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);
    }
 
    public static void main(String[] args)
    {
        // Test case #1:
        //     .....*.
        //     ...#...
        //     .*.#.*.
        //     .......
 
        char A[][]
            = { { '.', '.', '.', '.', '.', '*', '.' },
                { '.', '.', '.', '#', '.', '.', '.' },
                { '.', '*', '.', '#', '.', '*', '.' },
                { '.', '.', '.', '.', '.', '.', '.' } };
 
        r = 4;
        c = 7;
        System.out.println("The given grid : ");
 
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                System.out.print(A[i][j] + " ");
                arr[i][j] = A[i][j];
            }
            System.out.println();
        }
 
        // - initialization
        // - pre-calculations
        init();
 
        int ans = solve(0, 1);
        System.out.print(
            "Minimum distance for the given grid : ");
        System.out.println(ans);
        // Test Case #2
        //     ...#...
        //     ...#.*.
        //     ...#...
        //     .*.#.*.
        //     ...#...
 
        char Arr[][]
            = { { '.', '.', '.', '#', '.', '.', '.' },
                { '.', '.', '.', '#', '.', '*', '.' },
                { '.', '.', '.', '#', '.', '.', '.' },
                { '.', '*', '.', '#', '.', '*', '.' },
                { '.', '.', '.', '#', '.', '.', '.' } };
 
        r = 5;
        c = 7;
        System.out.println("The given grid : ");
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                System.out.print(Arr[i][j] + " ");
                arr[i][j] = Arr[i][j];
            }
            System.out.println();
        }
 
        // - initialization
        // - pre-calculations
        init();
        ans = solve(0, 1);
        System.out.print(
            "Minimum distance for the given grid : ");
        if (ans >= INF)
            System.out.println("not possible");
        else
            System.out.println(ans);
    }
}
 
// This code is contributed by anskalyan3.


Python3




import sys
import math
from collections import deque
 
INF = 99999999
MAXR = 12
MAXC = 12
MAXMASK = 2048
MAXHOUSE = 12
 
# stores distance taking source
# as every dirty tile
dist = [[[INF for _ in range(MAXHOUSE)]
         for _ in range(MAXC)] for _ in range(MAXR)]
 
# memoization for dp states
dp = [[-1 for _ in range(MAXMASK)] for _ in range(MAXHOUSE)]
 
# stores coordinates for
# dirty tiles
dirty = []
 
# Directions
X = [-1, 0, 0, 1]
Y = [0, 1, -1, 0]
 
arr = [['' for _ in range(21)] for _ in range(21)]
 
# len : number of dirty tiles + 1
# limit : 2 ^ len -1
# r, c : number of rows and columns
len, limit, r, c = 0, 0, 0, 0
 
# Returns true if current position
# is safe to visit
# else returns false
# Time Complexity : O(1)
 
 
def safe(x, 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)
 
 
def getDist(idx):
    # visited array to track visited cells
    vis = [[False for _ in range(21)] for _ in range(21)]
 
    # getting current position
    cx, cy = dirty[idx]
 
    # initializing queue for bfs
    pq = deque()
    pq.append((cx, cy))
 
    # initializing the dist to max
    # because some cells cannot be visited
    # by taking source cell as idx
    for i in range(r+1):
        for j in range(c+1):
            dist[i][j][idx] = INF
 
    # base conditions
    vis[cx][cy] = True
    dist[cx][cy][idx] = 0
 
    while pq:
        x = pq.popleft()
        for i in range(4):
            cx = x[0] + X[i]
            cy = x[1] + Y[i]
            if safe(cx, cy):
                if vis[cx][cy]:
                    continue
                vis[cx][cy] = True
                dist[cx][cy][idx] = dist[x[0]][x[1]][idx] + 1
                pq.append((cx, cy))
 
# Dynamic Programming state transition recursion
# with memoization. Time Complexity: O(n*n*2 ^ n)
 
 
def solve(idx, mask):
    # goal state
    if mask == limit:
        return dist[0][0][idx]
 
    # if already visited state
    if dp[idx][mask] != -1:
        return dp[idx][mask]
 
    ret = float('inf')
 
    # state transition relation
    for i in range(len):
        if (mask & (1 << i)) == 0:
            new_mask = mask | (1 << i)
            ret = min(ret, solve(i, new_mask) +
                      dist[dirty[i][0]][dirty[i][1]][idx])
 
    # adding memoization and returning
    dp[idx][mask] = ret
    return ret
 
 
def init():
    global dirty, dirty_count, arr, r, c, LIMIT_MASK
    dirty = []
    dirty_count = 0  # initialize the variable before using
    for i in range(r):
        for j in range(c):
            if (arr[i][j] == '*'):
                dirty.append((i, j))
                dirty_count += 1
    dirty_length = dirty_count
    LIMIT_MASK = (1 << dirty_count) - 1
 
 
if __name__ == "__main__":
    # Test case #1:
    #     .....*.
    #     ...#...
    #     .*.#.*.
    #     .......
 
    A = [['.', '.', '.', '.', '.', '*', '.'],
         ['.', '.', '.', '#', '.', '.', '.'],
         ['.', '*', '.', '#', '.', '*', '.'],
         ['.', '.', '.', '.', '.', '.', '.']
         ]
 
    r = 4
    c = 7
 
    print("The given grid : ")
 
    for i in range(r):
        for j in range(c):
            print(A[i][j], end=' ')
            arr[i][j] = A[i][j]
        print()
 
    # - initialization
    # - precalculations
    init()
 
    ans = solve(0, 1)
 
    print("Minimum distance for the given grid : ", end='')
    print(ans)
 
    # Test Case #2
    #     ...#...
    #     ...#.*.
    #     ...#...
    #     .*.#.*.
    #     ...#...
 
    Arr = [['.', '.', '.', '#', '.', '.', '.'],
           ['.', '.', '.', '#', '.', '*', '.'],
           ['.', '.', '.', '#', '.', '.', '.'],
           ['.', '*', '.', '#', '.', '*', '.'],
           ['.', '.', '.', '#', '.', '.', '.']
           ]
 
    r = 5
    c = 7
 
    print("The given grid : ")
 
    for i in range(r):
        for j in range(c):
            print(Arr[i][j], end=' ')
            arr[i][j] = Arr[i][j]
        print()
 
    # - initialization
    # - precalculations
    init()
    ans = solve(0, 1)
    print("Minimum distance for the given grid : ", end='')
    if ans >= INF:
        print("not possible")
    else:
        print(ans)


Javascript




const INF = 99999999;
const MAXR = 12;
const MAXC = 12;
const MAXMASK = 2048;
const MAXHOUSE = 12;
 
let dist = new Array(MAXR);
for (let i = 0; i < MAXR; i++) {
    dist[i] = new Array(MAXC);
    for (let j = 0; j < MAXC; j++) {
        dist[i][j] = new Array(MAXHOUSE);
    }
}
 
let dp = new Array(MAXHOUSE);
for (let i = 0; i < MAXHOUSE; i++) {
    dp[i] = new Array(MAXMASK);
}
 
let dirty = [];
 
let X = [-1, 0, 0, 1];
let Y = [0, 1, -1, 0];
 
let arr = new Array(21);
for (let i = 0; i < 21; i++) {
    arr[i] = new Array(21);
}
 
let len, limit, r, c;
 
function safe(x, y) {
    if (x >= r || y >= c || x < 0 || y < 0) return false;
    if (arr[x][y] == '#') return false;
    return true;
}
 
function getDist(idx) {
    let vis = new Array(21);
    for (let i = 0; i < 21; i++) {
        vis[i] = new Array(21);
    }
 
    let cx = dirty[idx].first;
    let cy = dirty[idx].second;
 
    let pq = [];
    pq.push({ first: cx, second: cy });
 
    for (let i = 0; i <= r; i++)
        for (let j = 0; j <= c; j++)
            dist[i][j][idx] = INF;
 
    vis[cx][cy] = true;
    dist[cx][cy][idx] = 0;
 
    while (pq.length > 0) {
        let x = pq.shift();
        for (let 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({ first: cx, second: cy });
            }
        }
    }
}
 
function solve(idx, mask) {
    if (mask == limit) return dist[0][0][idx];
 
    if (dp[idx][mask] != -1) return dp[idx][mask];
 
    let ret = Number.MAX_VALUE;
    for (let i = 0; i < len; i++) {
        if ((mask & (1 << i)) == 0) {
            let newMask = mask | (1 << i);
            ret = Math.min(
                ret,
                solve(i, newMask) +
                    dist[dirty[i].first][dirty[i].second][idx]
            );
        }
    }
 
    return (dp[idx][mask] = ret);
}
 
function init() {
    for (let i = 0; i < MAXHOUSE; i++) {
        for (let j = 0; j < MAXMASK; j++) {
            dp[i][j] = -1;
        }
    }
 
    dirty = [];
 
    for (let i = 0; i < r; i++)
        for (let j = 0; j < c; j++) {
            if (arr[i][j] == '*')
                dirty.push({ first: i, second: j });
        }
 
    dirty.unshift({ first: 0, second: 0 });
 
    len = dirty.length;
 
    limit = (1 << len) - 1;
 
    for (let i = 0; i < len; i++) getDist(i);
}
 
function main() {
    let A = [
        ['.', '.', '.', '.', '.', '*', '.'],
        ['.', '.', '.', '#', '.', '.', '.'],
        ['.', '*', '.', '#', '.', '*', '.'],
        ['.', '.', '.', '.', '.', '.', '.']
    ];
 
    r = 4;
    c = 7;
 
    console.log('The given grid : ');
 
    for (let i = 0; i < r; i++) {
        for (let j = 0; j < c; j++) {
            process.stdout.write(A[i][j] + ' ');
            arr[i][j] = A[i][j];
        }
        console.log("   ");
    }
     
    init();
 
    let ans = solve(0, 1);
 
    console.log('Minimum distance for the given grid : ' + ans);
     
 
    let Arr = [
        ['.', '.', '.', '#', '.', '.', '.'],
        ['.', '.', '.', '#', '.', '*', '.'],
        ['.', '.', '.', '#', '.', '.', '.'],
        ['.', '*', '.', '#', '.', '*', '.'],
        ['.', '.', '.', '#', '.', '.', '.']
    ];
 
    r = 5;
    c = 7;
 
    console.log('The given grid : ');
 
    for (let i = 0; i < r; i++) {
        for (let j = 0; j < c; j++) {
            process.stdout.write(Arr[i][j] + ' ');
            arr[i][j] = Arr[i][j];
        }
        console.log();
    }
 
    init();
    ans = solve(0, 1);
    console.log('\nMinimum distance for the given grid : ');
    if (ans >= INF) console.log('not possible\n');
    else console.log(ans + '\n');
 
    return 0;
}
 
main();


C#




using System;
using System.Collections.Generic;
 
class pair {
    public int first;
    public int second;
    public pair(int first, int second)
    {
        this.first = first;
        this.second = second;
    }
}
 
class GFG {
    static int INF = 99999999;
    static int MAXR = 12;
    static int MAXC = 12;
    static int MAXMASK = 2048;
    static int MAXHOUSE = 12;
 
    // stores distance taking source
    // as every dirty tile
    static int[][][] dist = new int[MAXR][][];
 
    // memoization for dp states
    static int[, ] dp = new int[MAXHOUSE, MAXMASK];
 
    // stores coordinates for
    // dirty tiles
    static List<pair> dirty = new List<pair>();
 
    // Direction
    static int[] X = { -1, 0, 0, 1 };
    static int[] Y = { 0, 1, -1, 0 };
    static char[, ] arr = new char[21, 21];
 
    // len : number of dirty tiles + 1
    // limit : 2 ^ len -1
    // r, c : number of rows and columns
    static int len, limit, r, c;
 
    // Returns true if current position
    // is safe to visit
    // else returns false
    // Time Complexity : O(1)
    public static bool safe(int x, int y)
    {
        if (x >= r || y >= c || x < 0 || 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)
    public static void getDist(int idx)
    {
 
        // visited array to track visited cells
        bool[, ] vis = new bool[21, 21];
 
        // getting current position
        int cx = dirty[idx].first;
        int cy = dirty[idx].second;
 
        // initializing queue for bfs
        Queue<pair> pq = new Queue<pair>();
        pq.Enqueue(new pair(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 condition
        vis[cx, cy] = true;
        dist[cx][cy][idx] = 0;
 
        while (pq.Count > 0) {
            pair x = pq.Peek();
            pq.Dequeue();
            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.Enqueue(new pair(cx, cy));
                }
            }
        }
    }
 
    // Dynamic Programming state transition recursion
    // with memoization. Time Complexity: O(n*n*2 ^ n)
    public static 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 = INF;
 
        // state transition rlation
        for (int i = 0; i < len; i++) {
            if ((mask & (1 << i)) == 0) {
                int newMask = mask | (1 << i);
                ret = Math.Min(
                    ret, solve(i, newMask)
                             + dist[dirty[i].first]
                                   [dirty[i].second][idx]);
            }
        }
 
        // adding memoization and returning
        return dp[idx, mask] = ret;
    }
    public static void init()
    {
 
        for (int i = 0; i < MAXR; i++) {
            dist[i] = new int[MAXC][];
            for (int j = 0; j < MAXC; j++) {
                dist[i][j] = new int[MAXHOUSE];
            }
        }
 
        // initializing containers
        for (int i = 0; i < MAXHOUSE; i++) {
            for (int j = 0; j < MAXMASK; j++) {
                dp[i, j] = -1;
            }
        }
 
        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.Add(new pair(i, j));
            }
        }
 
        // inserting ronot's location at the
        // beginning of the dirty tile
        dirty.Insert(0, new pair(0, 0));
        len = dirty.Count;
 
        // 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);
    }
 
    public static void Main(string[] args)
    {
 
        // Test case #1:
        //     .....*.
        //     ...#...
        //     .*.#.*.
        //     .......
        char[][] A = new char[4][] {
            new char[] { '.', '.', '.', '.', '.', '*',
                         '.' },
            new char[] { '.', '.', '.', '#', '.', '.',
                         '.' },
            new char[] { '.', '*', '.', '#', '.', '*',
                         '.' },
            new char[] { '.', '.', '.', '.', '.', '.', '.' }
        };
 
        r = 4;
        c = 7;
        Console.WriteLine("The given grid : ");
 
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                Console.Write(A[i][j] + " ");
                arr[i, j] = A[i][j];
            }
            Console.WriteLine();
        }
 
        // - initialization
        // - pre-calculations
        init();
 
        int ans = solve(0, 1);
        Console.Write(
            "Minimum distance for the given grid : ");
        Console.WriteLine(ans);
 
        // Test Case #2
        //     ...#...
        //     ...#.*.
        //     ...#...
        //     .*.#.*.
        //     ...#...
        char[][] Arr = new char[5][] {
            new char[] { '.', '.', '.', '#', '.', '.',
                         '.' },
            new char[] { '.', '.', '.', '#', '.', '*',
                         '.' },
            new char[] { '.', '.', '.', '#', '.', '.',
                         '.' },
            new char[] { '.', '*', '.', '#', '.', '*',
                         '.' },
            new char[] { '.', '.', '.', '#', '.', '.', '.' }
        };
        r = 5;
        c = 7;
        Console.WriteLine("The given grid: ");
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                Console.Write(Arr[i][j] + " ");
                arr[i, j] = Arr[i][j];
            }
            Console.WriteLine();
        }
 
        // - initialization
        // - pre-calculations
        init();
        ans = solve(0, 1);
        Console.Write(
            "Minimum distance for the given grid: ");
        if (ans >= INF)
            Console.WriteLine("not possible");
        else
            Console.WriteLine(ans);
    }
}


Output: 
 

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

Note:
We have used the initial state to be dp[0][1] because we have pushed the start location at the first position in the container of houses. Hence, our Bit Mask will be 1 as the 0th bit is set i.e we have visited the starting location for our trip.

Time Complexity: O(n2 * 2n)
Consider the number of houses to be n. So, there are n * (2n) states and at every state, we are looping over n houses to transit over to next state and because of memoization we are doing this looping transition only once for each state.
Auxiliary space: O(n * 2n), due to the memoization of the dp states.

Recommended
 

 



Last Updated : 25 Apr, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads