【问题标题】:Dynamic Programming Problem: Maximum Accessible Area On A 2D Grid动态规划问题:二维网格上的最大可访问区域
【发布时间】:2020-10-06 02:28:58
【问题描述】:

我最近遇到了这个动态编程问题,我想看看是否有人对解决这个问题的方法有想法。我遇到了困难,这可能是因为我陷入了创建一棵巨大的树来处理所有可能性的想法。

问题

有一个角色可以在二维 (x,y) 上移动 坐标网格。角色被放置在点 (0,0)。

角色可以从 (x, y) 移动到 (x+1, y), (x-1, y), (x, y+1), 和 (x, y-1)。

有些地点很危险,并且有地雷。要知道哪些点 是安全的,我们检查 abs(x) 的数字之和是否加上 abs(y)的位数小于等于23。

例如,点 (64, -59) 不安全,因为 6 + 4 + 5 + 9 = 24,大于 23。点 (105, -17) 是安全的,因为 1

  • 0 + 5 + 1 + 7 = 14,小于 23。

角色可以访问的区域有多大?

【问题讨论】:

  • 坐标网格有什么限制吗?否则安全区显然是无限的(1 万亿 x 1 万亿 = 2,依此类推) 安全区应该是相邻的吗?另一个问题,实际上是一个0x0点吗?角色肯定可以通过这条线无限移动,并且总是得到 nx0 = 0(安全方块)
  • 这些点是三角形的一部分吗? (319,219), (398,298) ? i.stack.imgur.com/ToNGJ.png
  • 你是如何在没有经过 23 的 599 的情况下达到 698 的?
  • 您没有减去您方格中被地雷覆盖的区域,或者由于被地雷包围而无法进入的区域。
  • @HikmatFarhat 599 是可以的,因为数字的总和必须小于或等于 23,所以数字加起来是 23 是可以的,但不是24.

标签: algorithm dynamic-programming


【解决方案1】:

对-1000, -1000 ... 1000, 1000字段最大值有相同的假设,这里是用C#编写的简单BFS版本

检查点是否安全的功能

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

数字总和(假设非负输入)

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

BFS算法

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

输出

592597

发现可达单元格后可以看到图

【讨论】:

    【解决方案2】:

    让我们写一个简单的DFS。

    首先,让我们考虑一下可以访问多少个单元格,因此我们的算法不会永远运行。左上角在 (999, 999) 中,右下角在 (-999, -999) 中,你不能超过外面的正方形,因为正方形一侧的坐标之一将是 ±999,但 9 + 9 + 9 > 23。这个正方形内有 1999 * 1999 个点,所以 DFS 会很快。

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

    这给了我们592597。

    【讨论】:

    • 奇怪,我使用 BFS 得到了592597
    • @tym32167 抱歉,此代码在网格上做了对角线移动,已修复,也得到了 592597
    • @tym32167 确定为什么不
    • 嘿,我对斜线移动也有同样的错误。但无论如何,这似乎不影响结果。
    • @dwx dfs/bfs 是典型的图算法。互联网上有很多关于该算法的信息。您只需要稍微不同地考虑坐标网格,其中每个单元格都是一个图形节点,并且只有好的单元格(
    【解决方案3】:

    我的回答在这里有点太长了,所以我在HackerNoon 的一篇文章中记录了一个 Java 解决方案以及思考过程和对坐标系的详细解释。

    【讨论】:

      【解决方案4】:

      这是来自https://hackernoon.com/how-to-find-the-maximum-accessible-area-on-a-2d-grid-rhy3eiv的Java代码的JS版本 想法是一样的。返回 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;
      }

      【讨论】:

        【解决方案5】:

        抱歉回答晚了,这里是上述问题解决方案最简单的JS版本:

             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();
        

        【讨论】:

          猜你喜欢
          • 2021-12-23
          • 2011-09-23
          • 2011-10-17
          • 1970-01-01
          • 2012-04-09
          • 1970-01-01
          • 2011-10-14
          相关资源
          最近更新 更多