Dynamic Programming Problem: Maximum Accessible Area On A 2D Grid

Viewed 907

I recently encountered this dynamic programming problem and I wanted to see if anyone had thoughts on the approach to solve this. I had a hard time with this, and it's probably because I got stuck in the thinking of creating a huge tree to handle all of the possibilities.

The Problem

There is a character who can move around on an two-dimensional (x,y) coordinate grid. The character is placed at point (0,0).

From (x, y) the character can move to (x+1, y), (x-1, y), (x, y+1), and (x, y-1).

Some points are dangerous and contain land mines. To know which points are safe, we check whether the sum of the digits of abs(x) plus the sum of the digits of abs(y) are less than or equal to 23.

For example, the point (64, -59) is not safe because 6 + 4 + 5 + 9 = 24, which is greater than 23. The point (105, -17) is safe because 1

  • 0 + 5 + 1 + 7 = 14, which is less than 23.

How large is the area that the character can access?

5 Answers

Having same assumption regarding -1000, -1000 ... 1000, 1000 field max, here is simple BFS version written with C#

Function to check if point is safe

bool isSafe(int x, int y){
    return (sum(Math.Abs(x)) + sum(Math.Abs(y))) < 24;
}

Sum of digits (assuming non negative input)

int sum(int n){
    int ret = 0;        
    while(n > 0){
        ret += n%10;
        n = n/10;
    }
    return ret;
}

BFS algorithm

var visited = new bool[2000, 2000];
var queue = new Queue<(int x, int y)>();

var steps = new List<(int di, int dj)>() {(-1, 0), (1, 0), (0, -1), (0, 1)};

int count = 1; 

queue.Enqueue((0, 0));
visited[1000, 1000] = true;

while (queue.Count != 0)
{
    var point = queue.Dequeue();
    foreach (var step in steps)
        if (!visited[point.x + step.di + 1000, point.y + step.dj + 1000] && isSafe(point.x + step.di, point.y + step.dj))
        {
            count++;
            visited[point.x + step.di + 1000, point.y + step.dj + 1000] = true;
            queue.Enqueue((point.x + step.di, point.y + step.dj));
        }
}

Console.WriteLine(count);   

Output

592597

After discovering reachable cells you can see figure

...

Let's just write a simple DFS.

At first, let's think how many cells can be visited, so our algorithm won't run forever. You can't go further than outside square with a top-left corner in (999, 999) and bottom-right corner in (-999, -999), because one of coordinates on the square side will be ±999, but 9 + 9 + 9 > 23. There are 1999 * 1999 < 4M points inside this square so DFS will be quite fast.

#include <cmath>
#include <iostream>

const int SQUARE_SIDE = 1'000;

// v[i][j] is true if cell (i - SQUARE-SIDE, j - SQUARE_SIDE) was already
// visited during DFS, false otherwise.
bool v[2 * SQUARE_SIDE][2 * SQUARE_SIDE];
int answer = 0;

const struct {
  int dx;
  int dy;
} moves[] = {
    {1, 0},   // Up
    {-1, 0},  // Down
    {0, -1},  // Left
    {0, 1}    // Right
};

int calc_abs_sum(int n) {
  n = abs(n);
  int sum = 0;
  while (n) {
    sum += n % 10;
    n /= 10;
  }
  return sum;
}

void dfs(int x, int y) {
  // Add SQUARE_SIDE to make indices non-negative.
  v[x + SQUARE_SIDE][y + SQUARE_SIDE] = true;
  answer++;

  for (const auto &move : moves)
    if (calc_abs_sum(x + move.dx) + calc_abs_sum(y + move.dy) <= 23 &&
        !v[x + move.dx + SQUARE_SIDE][y + move.dy + SQUARE_SIDE]) {
      dfs(x + move.dx, y + move.dy);
    }
}

int main() {
  dfs(0, 0);
  std::cout << answer << std::endl;
}

which gives us 592597.

My answer was getting a bit too long for here so I documented a Java solution along with the thought process and a detailed explanation on the coordinate system in an article over at HackerNoon.

Here is JS version of Java code from https://hackernoon.com/how-to-find-the-maximum-accessible-area-on-a-2d-grid-rhy3eiv Idea is the same. Returns 592597.

const safePosition = 23;
const axisLength = 1000; 


const hasEMPMine = (x, y)=>{
  let tempX = Math.abs(x)
  let tempY = Math.abs(y)
  let sumX = 0;
    let sumY = 0;

  while(tempX > 0){
    sumX += tempX%10;
    tempX = Math.floor(tempX/10);
  }

  while(tempY>0){
    sumY +=tempY%10;
    tempY = Math.floor(tempY/10);
  }

  return sumX +sumY > safePosition;
}

function Create2DArray(rows, cols, defaultValue) {
    var arr = [];
  
    for(var i=0; i < rows; i++){
  
        arr.push([]);
  
        arr[i].push( new Array(cols));
  
        for(var j=0; j < cols; j++){
          arr[i][j] = defaultValue;
        }
    }
  
  return arr;
}

const CharacterGraphArea = () => {
  const booleanGraph = Create2DArray(2000, 2000, false);
  const characterLocationsArray = [];

  const possibleMovesArray = [{x: -1, y: 0}, {x:1, y:0}, {x:0, y:1}, {x:0, y: -1}]

  characterLocationsArray.push({x:0, y: 0});
  booleanGraph[0+axisLength][0+axisLength] = true;
  let area = 1;

  while(characterLocationsArray.length!==0){
    let lastLocation = characterLocationsArray.pop();
    possibleMovesArray.forEach(move=>      {
      let newX = lastLocation.x + move.x;
      let newY = lastLocation.y + move.y;
      let isSafe = !hasEMPMine(newX, newY);

      let x = newX + axisLength;
      let y = newY + axisLength;

      if(!booleanGraph[x][y] && isSafe){
        booleanGraph[x][y] = true;
        area = area + 1;
        characterLocationsArray.push({x:newX, y:newY});
      }});
  }

  return area;
}

Sorry for late Answer, Here is the simplest JS version of the above problem solution:

     let danger_point = 24;

     function abs_sum(value){ // function to get abs sum
        value = Math.abs(value)
        let sum = 0;

        while (value) {
            sum += value % 10;
            value = Math.floor(value / 10);
        }
        return sum;
     }

     function is_safe(x, y){ // function to check if current point is safe
         return (abs_sum(x)+abs_sum(y) < danger_point)?true:false;
     }
    // We are simply visiting first quadrant only one by one point. this will check if the point is safe, it will simply add to the safe_points counter....
    function findPoints(){           
        let area = 1000;
        let total_points = 0, safe_points = 0;  
        for(let y=0; y<=area; y++){ // Y-AXIS Loop 
            for(let x=0; x<=area; x++){  // X-AXIS Loop                    
                if(is_safe(x,y)){
                    safe_points++;                         
                } 
                ++total_points;                   
            } 
        }
        // As there are 4 quadrants, we will multiply the result by 4
        console.log(`total points = ${total_points*4}`)
        console.log(`safe points = ${safe_points*4}`)
        
    }
    
    findPoints();
Related