【问题标题】:Bomb dropping algorithm炸弹投放算法
【发布时间】:2013-02-24 08:39:28
【问题描述】:

我有一个由非负整数组成的n x m 矩阵。例如:

2 3 4 7 1
1 5 2 6 2
4 3 4 2 1
2 1 2 4 1
3 1 3 4 1
2 1 4 3 2
6 9 1 6 4

“投掷炸弹”将目标单元格及其所有八个相邻单元格的数量减少一,最小为零。

x x x 
x X x
x x x

什么算法可以确定将所有单元格减少到零所需的最少炸弹数量?

B 选项(由于我不是一个细心的读者)

实际上,第一个版本的问题并不是我要寻找的答案。我没有仔细阅读整个任务,还有额外的限制,让我们说:

如果行中的序列必须是非递增的,那么简单的问题呢:

8 7 6 6 5 是可能的输入序列

7 8 5 5 2 是不可能的,因为 7 -> 8 依次增长。

也许为“更简单”的案例找到答案将有助于为更难的案例找到解决方案。

PS:我相信当我们有几个相同的情况需要最少的炸弹来清除上线时,我们会选择在行的“左侧”使用最多炸弹的一个。还有任何可能是正确的证据吗?

【问题讨论】:

  • 好吧,我只是发现有些字段可以像示例中那样跳过 2 3 1 5 将其放在 2,3,1 上是没有意义的,因为放在它们上会导致一些子集损坏,我们可以通过放弃 5. 但找不到如何让它在全球范围内工作(如果它是正确的方式)。清除 2 需要使用 2 颗投在任何邻居身上的炸弹,而 5 则包含其他组的伤害。但是我不知道以后要做什么,因为当您重写它时(减少后),那么您有两个选择(没有一个超级组损坏)。
  • 这是 NP 难的吗?它看起来是Maximum Coverage Problem 的变体。
  • 这看起来有点像扫雷,除了你可以在一个点上多次放置炸弹,数字只表示在一个点上和周围的最小炸弹数量,而不是确切的数量。跨度>
  • 也许你应该澄清一下,你说的问题是:what's the minimum amount of bombs required to clean the board?这是否意味着不一定需要找到实际的轰炸模式,而只需要找到最少数量的炸弹?
  • @us2012:找到可能的炸弹数量的下限和上限相当容易,如果它们匹配,则必须是所需的确切炸弹数量,无需计算即可找到实际的模式。但这种情况可能只会发生一次,如果有的话。

标签: algorithm language-agnostic matrix


【解决方案1】:

最慢但最简单且无错误的算法是生成并测试所有有效的可能性。对于这种情况非常简单(因为结果与炸弹放置的顺序无关)。

  1. 创建N次应用bomp的函数
  2. 为所有 bompb-placement/bomb-count 可能性创建循环(当 matrix==0 时停止)
  3. 永远记住最好的解决方案。
  4. 在循环结束时,您有最佳解决方案
    • 不仅是炸弹的数量,还有它们的位置

代码可能如下所示:

void copy(int **A,int **B,int m,int n)
    {
    for (int i=0;i<m;i++)
     for (int j=0;i<n;j++)
       A[i][j]=B[i][j];
    }

bool is_zero(int **M,int m,int n)
    {
    for (int i=0;i<m;i++)
     for (int j=0;i<n;j++)
      if (M[i][j]) return 0;
    return 1;
    }

void drop_bomb(int **M,int m,int n,int i,int j,int N)
    {
    int ii,jj;
    ii=i-1; jj=j-1; if ((ii>=0)&&(ii<m)&&(jj>=0)&&(jj<n)&&(M[ii][jj])) { M[ii][jj]-=N; if (M[ii][jj]<0) M[ii][jj]=0; }
    ii=i-1; jj=j  ; if ((ii>=0)&&(ii<m)&&(jj>=0)&&(jj<n)&&(M[ii][jj])) { M[ii][jj]-=N; if (M[ii][jj]<0) M[ii][jj]=0; }
    ii=i-1; jj=j+1; if ((ii>=0)&&(ii<m)&&(jj>=0)&&(jj<n)&&(M[ii][jj])) { M[ii][jj]-=N; if (M[ii][jj]<0) M[ii][jj]=0; }
    ii=i  ; jj=j-1; if ((ii>=0)&&(ii<m)&&(jj>=0)&&(jj<n)&&(M[ii][jj])) { M[ii][jj]-=N; if (M[ii][jj]<0) M[ii][jj]=0; }
    ii=i  ; jj=j  ; if ((ii>=0)&&(ii<m)&&(jj>=0)&&(jj<n)&&(M[ii][jj])) { M[ii][jj]-=N; if (M[ii][jj]<0) M[ii][jj]=0; }
    ii=i  ; jj=j+1; if ((ii>=0)&&(ii<m)&&(jj>=0)&&(jj<n)&&(M[ii][jj])) { M[ii][jj]-=N; if (M[ii][jj]<0) M[ii][jj]=0; }
    ii=i+1; jj=j-1; if ((ii>=0)&&(ii<m)&&(jj>=0)&&(jj<n)&&(M[ii][jj])) { M[ii][jj]-=N; if (M[ii][jj]<0) M[ii][jj]=0; }
    ii=i+1; jj=j  ; if ((ii>=0)&&(ii<m)&&(jj>=0)&&(jj<n)&&(M[ii][jj])) { M[ii][jj]-=N; if (M[ii][jj]<0) M[ii][jj]=0; }
    ii=i+1; jj=j+1; if ((ii>=0)&&(ii<m)&&(jj>=0)&&(jj<n)&&(M[ii][jj])) { M[ii][jj]-=N; if (M[ii][jj]<0) M[ii][jj]=0; }
    }

void solve_problem(int **M,int m,int n)
    {
    int i,j,k,max=0;
    // you probably will need to allocate matrices P,TP,TM yourself instead of this:
    int P[m][n],min;             // solution: placement,min bomb count
    int TM[m][n],TP[m][n],cnt;   // temp
    for (i=0;i<m;i++)            // max count of bomb necessary to test
     for (j=0;j<n;j++)
      if (max<M[i][j]) max=M[i][j];
    for (i=0;i<m;i++)            // reset solution
     for (j=0;j<n;j++)
      P[i][j]=max;
    min=m*n*max; 
        copy(TP,P,m,n); cnt=min;

    for (;;)  // generate all possibilities
        {
        copy(TM,M,m,n);
        for (i=0;i<m;i++)   // test solution
         for (j=0;j<n;j++)
          drop_bomb(TM,m,n,TP[i][j]);
        if (is_zero(TM,m,n))// is solution
         if (min>cnt)       // is better solution -> store it
            {
            copy(P,TP,m,n); 
            min=cnt;    
            }
        // go to next possibility
        for (i=0,j=0;;)
            {
            TP[i][j]--;
            if (TP[i][j]>=0) break;
            TP[i][j]=max;
                 i++; if (i<m) break;
            i=0; j++; if (j<n) break;
            break;
            }
        if (is_zero(TP,m,n)) break;
        }
    //result is in P,min
    }

这可以通过多种方式进行优化,...最简单的方法是使用 M 矩阵重置解决方案,但您需要更改最大值以及 TP[][] 减量代码

【讨论】:

    【解决方案2】:

    到目前为止,有几个答案给出了指数时间,其中一些涉及动态编程。我怀疑这些是否有必要。

    我的解决方案是O(mnS),其中m, n 是棋盘的尺寸,S 是所有整数的总和。这个想法相当蛮力:找到每次杀死最多的位置并在0处终止。

    它为给定的棋盘提供 28 步棋,并且在每次掉落后打印出棋盘。

    完整、不言自明的代码:

    import java.util.Arrays;
    
    public class BombMinDrops {
    
        private static final int[][] BOARD = {{2,3,4,7,1}, {1,5,2,6,2}, {4,3,4,2,1}, {2,1,2,4,1}, {3,1,3,4,1}, {2,1,4,3,2}, {6,9,1,6,4}};
        private static final int ROWS = BOARD.length;
        private static final int COLS = BOARD[0].length;
        private static int remaining = 0;
        private static int dropCount = 0;
        static {
            for (int i = 0; i < ROWS; i++) {
                for (int j = 0; j < COLS; j++) {
                    remaining = remaining + BOARD[i][j];
                }
            }
        }
    
        private static class Point {
            int x, y;
            int kills;
    
            Point(int x, int y, int kills) {
                this.x = x;
                this.y = y;
                this.kills = kills;
            }
    
            @Override
            public String toString() {
                return dropCount + "th drop at [" + x + ", " + y + "] , killed " + kills;
            }
        }
    
        private static int countPossibleKills(int x, int y) {
            int count = 0;
            for (int row = x - 1; row <= x + 1; row++) {
                for (int col = y - 1; col <= y + 1; col++) {
                    try {
                        if (BOARD[row][col] > 0) count++;
                    } catch (ArrayIndexOutOfBoundsException ex) {/*ignore*/}
                }
            }
    
            return count;
        }
    
        private static void drop(Point here) {
            for (int row = here.x - 1; row <= here.x + 1; row++) {
                for (int col = here.y - 1; col <= here.y + 1; col++) {
                    try {
                        if (BOARD[row][col] > 0) BOARD[row][col]--;
                    } catch (ArrayIndexOutOfBoundsException ex) {/*ignore*/}
                }
            }
    
            dropCount++;
            remaining = remaining - here.kills;
            print(here);
        }
    
        public static void solve() {
            while (remaining > 0) {
                Point dropWithMaxKills = new Point(-1, -1, -1);
                for (int i = 0; i < ROWS; i++) {
                    for (int j = 0; j < COLS; j++) {
                        int possibleKills = countPossibleKills(i, j);
                        if (possibleKills > dropWithMaxKills.kills) {
                            dropWithMaxKills = new Point(i, j, possibleKills);
                        }
                    }
                }
    
                drop(dropWithMaxKills);
            }
    
            System.out.println("Total dropped: " + dropCount);
        }
    
        private static void print(Point drop) {
            System.out.println(drop.toString());
            for (int[] row : BOARD) {
                System.out.println(Arrays.toString(row));
            }
    
            System.out.println();
        }
    
        public static void main(String[] args) {
            solve();
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-13
      相关资源
      最近更新 更多