【问题标题】:Divide a grid into several possibilities将网格划分为几种可能性
【发布时间】:2022-10-15 02:21:55
【问题描述】:

大小为 m*n 的网格仅提供数字 1、2、3,其中 m 是行数,n 是列数。

将网格水平或垂直分成两部分,这样每个部分必须至少包含一个值为 2 的单元格。一旦划分,我们可以进一步划分以获得其他组合。 找出给定网格值可以划分多少个。

例如,如果输入网格:

1,2,3
2,1,2
3,1,1

输出是4

以下是 4 种可能性。

Possibility (1)
 1,2,3
-------
 2,1,2
 3,1,1
 
  |
2,|1,2
3,|1,1
  |
  
Possibility (2)
  
 1,2,3
-------
 2,1,2
 3,1,1
    
    |
2,1,|2
3,1,|1
    |
Possibility (3)
  |
1,|2,3
2,|1,2
3,|1,1
  |
 
  | 
2,|3
1,|2
1,|1  
  |
  
Possibility (4) 
  |
1,|2,3
2,|1,2
3,|1,1
  |
 
 2,3
-----
 1,2
 1,1 
 

如何在运行时复杂性方面有效地解决这个问题。

作为第一步,我通过在元素为 1 或 3 时将值设置为 0 来修改网格。然后在元素为 2 时将值设置为 1。因此我将其转换为二进制矩阵。但我无法找到一种方法来获得可能的组合。

public int process(int[][] grid) {
   int m = grid.length, n = grid[0].length;
   int result = 0;
   for(int i=0; i<m; i++) {
      for(int j=0; j<n; j++) {
         int e = grid[i][j];
         if(e == 2) grid[i][j] = 1;
         else grid[i][j] = 0;
      }
   }
   //.....todo...
   return result;
}

【问题讨论】:

  • 到目前为止,您尝试过什么,即使您认为它效率低下?
  • 你能继续分裂多远?在我看来,答案#1 可以继续划分 2x2 网格。而如果你可以一直分割,那么最小的不能分割的网格是什么? 1x1,或 1x2 和 2x1,还是什么?
  • 另外,当您说“高效”时,您是指从编程角度还是从运行时角度?
  • @DavidA,我现在用所需信息更新了帖子
  • 题外话:我还没有看到将其转换为二进制矩阵有何帮助。 if (mat [i][j] ==2)if (mat [i][j] == 1)if (mat [i][j]) 没有太大区别,其中mat 在前两个中是 int [][],在第三个中是 boolean [][]

标签: java algorithm matrix time-complexity


【解决方案1】:

我不知道我完全理解你的问题,但这是我的两分钱:

首先定义一个检查子网格是否包含至少一个 2 的方法:

public static boolean contains2(int[][] grid, int rs, int re, int cs, int ce) {
    for (int i = rs; i < re; i++) {
        for (int j = cs; j < ce; j++) {
            if (grid[i][j] == 2) {
                return true;
            }
        }
    }
    return false;
}

接下来,您可以定义一个返回子网格可能水平分割数的方法:

public static int splitCountH(int[][] grid, int rs, int re, int cs, int ce) {
    int result = 0;
    for (int i = rs+1; i < re; ++i) {
        if (contains2(grid, rs, i, cs, ce) && contains2(grid, i, re, cs, ce)) {
            ++result;
        }
    }
    return result;
}

另一个给出可能的垂直分割数:

public static int splitCountV(int[][] grid, int rs, int re, int cs, int ce) {
    int result = 0;
    for (int j = cs+1; j < ce; ++j) {
        if (contains2(grid, rs, re, cs, j) && contains2(grid, rs, re, j, ce)) {
            ++result;
        }
    }
    return result;
}

Next 方法给出了水平拆分后跟垂直拆分或垂直拆分后跟水平拆分的可能组合数:

public static int process(int[][] grid, int rs, int re, int cs, int ce) {
    int result = 0;
    for (int i = rs+1; i < re; ++i) {
        if (contains2(grid, rs, i, cs, ce) && contains2(grid, i, re, cs, ce)) {
            result += splitCountV(grid, rs, i, cs, ce)
                    + splitCountV(grid, i, re, cs, ce);
        }
    }
    for (int j = cs+1; j < ce; ++j) {
        if (contains2(grid, rs, re, cs, j) && contains2(grid, rs, re, j, ce)) {
            result += splitCountH(grid, rs, re, cs, j)
                    + splitCountH(grid, rs, re, j, ce);
        }
    }
    return result;
}

您的处理方法变为:

public static int process(int[][] grid) {
    return process(grid, 0, grid.length, 0, grid[0].length);
}

更新:

如果要授权两个水平拆分或两个垂直拆分,可以添加以下方法:

public static int splitCount(int[][] grid, int rs, int re, int cs, int ce) {
    return splitCountH(grid, rs, re, cs, ce)
            + splitCountV(grid, rs, re, cs, ce);
}

并修改process方法:

public static int process(int[][] grid, int rs, int re, int cs, int ce) {
    int result = 0;
    for (int i = rs+1; i < re; ++i) {
        if (contains2(grid, rs, i, cs, ce) && contains2(grid, i, re, cs, ce)) {
            result += splitCount(grid, rs, i, cs, ce)
                    + splitCount(grid, i, re, cs, ce);
        }
    }
    for (int j = cs+1; j < ce; ++j) {
        if (contains2(grid, rs, re, cs, j) && contains2(grid, rs, re, j, ce)) {
            result += splitCount(grid, rs, re, cs, j)
                    + splitCount(grid, rs, re, j, ce);
        }
    }
    return result;
}

但在这种情况下,您的示例将有 6 种可能性。

【讨论】:

  • @greybeard 好的,我已经更新了我的答案以支持两个垂直拆分或两个水平拆分,但在这种情况下有 6 种可能性。
  • (6 个不同的分割/分裂序列导致“单 2 部分”的五种不同配置:什么可能性?)
  • 没错,在这种情况下,它既是单 2 部分的五种不同配置,又是五种不同的划分图(忽略它们的绘制顺序)。但是对于一个由 2s 填充的 2x2 网格,只有一种单 2 部分配置,但有两种不同的划分图(水平或垂直不间断)。
【解决方案2】:

以下是 4 种可能性。

(1) (2) (3) (4)

1 2 3 1 2 3 1|2|3 1|2 3
----- ----- | | |---
2|1 2 2 1|2 2|1|2 2|1 2
 | | | | |
3|1 1 3 1|1 3|1|1 3|1 1

关于什么

1 2|3
---|
2 1|2
   |
3 1|1

?

【讨论】:

  • “规范”举例容易出错。
  • 我们必须水平划分,然后是下一个子划分。否则垂直划分然后细分。在那种情况下,我们只能得到 4 种可能性。此处提到的新组合无效。
  • “新组合”首先是垂直分成左两列和右列,包含一个 2 并且不能再整除。然后将左两列分成顶行和 2×2“底”子矩阵。 2 对 1 之于“可能性 (4)”。显示的每个部分都包含一个 2。如何这种组合会是不是有效的?如果它无效,并且您没有找到为什么在问题中,请添加它那里.
【解决方案3】:

递归将成为你的朋友。抱歉,我是用 Python 而不是 Java 做的,但算法是一样的。本质上,划分网格并测试以查看两侧是否有 2。如果有,递归每一边,直到没有。

    import itertools

    grid = [ 1, 2, 3
           , 2, 1, 2
           , 3, 1, 1 ]
    
    def print_horz( top, trows, bottom, brows, columns, depth ):
        for m in range( trows ):
            print( '    ' * depth, top[ m * columns : ( m + 1 ) * columns ] )
        print( '    ' * depth, '-' * columns )
        for m in range( brows ):
            print( '    ' * depth, bottom[ m * columns : ( m + 1 ) * columns ] )
        print()
    
    def print_vert( left, lcolumns, right, rcolumns, rows, depth ):
        for m in range( rows ):
            print( '    ' * depth
                , left[ m * lcolumns : ( m + 1 ) * lcolumns ]
                , '|'
                , right[ m * rcolumns : ( m + 1 ) * rcolumns ] )
        print()
    
    def divide_grid( grid, rows, columns, depth = 0 ):
        if grid.count( 2 ) < 2:
            return grid.count( 2 )
    
        #print( '    ' * depth, 'grid', grid, depth )
        for m in range( rows - 1 ):
            top = grid[ : ( m + 1 ) * columns ]
            bottom = grid[ ( m + 1 ) * columns : ]
            #print( '    ' * depth, 'horz {}/{}/{}'.format( m, rows - 1, depth), top, ( m + 1 ), bottom, rows - ( m + 1 ), columns )
            if 2 in top and 2 in bottom:
                print_horz( top, ( m + 1 ), bottom, rows - ( m + 1 ), columns, depth )
    
                divide_grid( top, ( m + 1 ), columns, depth + 1 )
                divide_grid( bottom, rows - ( m + 1 ), columns, depth + 1 )
    
        for n in range( columns - 1 ):
            left = list( itertools.chain.from_iterable(
                [ grid[ m * columns : ( m + 1 ) * columns ][ : ( n + 1 ) ] for m in range( rows ) ] ) )
            right = list( itertools.chain.from_iterable(
                [ grid[ m * columns : ( m + 1 ) * columns ][ ( n + 1 ) : ] for m in range( rows ) ] ) )
            #print( '    ' * depth, 'vert {}/{}/{}'.format( n, columns - 1, depth), left, ( n + 1 ), right, columns - ( n + 1 ), rows )
            if 2 in left and 2 in right:
                print_vert( left, ( n + 1 ), right, columns - ( n + 1 ), rows, depth )
    
                divide_grid( left, rows, ( n + 1 ), depth + 1 )
                divide_grid( right, rows, columns - ( n + 1 ), depth + 1 )
    
    divide_grid( grid, 3, 3 )

结果

 [1, 2, 3]
 ---
 [2, 1, 2]
 [3, 1, 1]

     [2] | [1, 2]
     [3] | [1, 1]

     [2, 1] | [2]
     [3, 1] | [1]

 [1] | [2, 3]
 [2] | [1, 2]
 [3] | [1, 1]

     [2, 3]
     --
     [1, 2]
     [1, 1]

     [2] | [3]
     [1] | [2]
     [1] | [1]

 [1, 2] | [3]
 [2, 1] | [2]
 [3, 1] | [1]

     [1, 2]
     --
     [2, 1]
     [3, 1]

     [1] | [2]
     [2] | [1]
     [3] | [1]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-18
    • 1970-01-01
    • 1970-01-01
    • 2020-06-20
    相关资源
    最近更新 更多