【问题标题】:Finding a minimal set of rectangles covering a binary matrix [duplicate]找到覆盖二进制矩阵的最小矩形集[重复]
【发布时间】:2012-07-17 17:02:11
【问题描述】:

假设我有以下二进制矩阵:

0 0 0 1 1 1 0
0 0 0 1 1 1 0
0 0 0 1 1 1 0
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
0 0 0 1 1 1 0
0 0 0 1 1 1 0
0 0 0 1 1 1 0

我想找到一组平行于 x 和 y 轴的矩形集,该矩形集覆盖每个 1至少一次,并且不覆盖具有最小基数的单个 0(最少的矩形)。在上面的示例中,这将是矩形 ((0, 3), (6, 5))((3, 0), (5, 8))(符号形式为 (topleft, bottomright)) - 最小的解决方案是使用两个矩形。

我之前的尝试是找到面积最大的矩形仅覆盖1,将该矩形添加到集合中,然后将所有1's标记为0,直到所有1消失了。虽然这个集合会覆盖每个 1 而不是单个 0,但它不一定有最小的基数(这个算法在上面的例子中会失败)。

【问题讨论】:

  • 最小解是指最少的矩形数还是最少的最小矩形数?或者最少数量的矩形将共同拥有最少数量的1's?
  • @YePhIcK:最小的矩形集意味着最少的矩形,无论大小。
  • 单个 1 可以是两个矩形的一部分还是两个矩形必须不相交??
  • @AmanDeepGautam:每个 1 必须是至少一个矩形的一部分,所以两个、三个等都可以。
  • 为什么要关闭它?这是一个 5 年前的问题,甚至不是您关闭它的答案的实际副本。我的问题确实允许重叠。

标签: algorithm matrix


【解决方案1】:

我认为您应该将覆盖的 1 替换为 2 而不是 0。这样,您可以在覆盖 1 时包含 2,但仍然不覆盖任何 0。

这是我想出的:

#include <stdio.h>
#include <stdlib.h>

struct board {
  int **data;
  int w,h;
};

int  load_board(char *, struct board *);
void print_board(struct board *);

int  max_height_with_fixed_w(struct board *board, int i, int j, int w) {
  int jj = -1, ii;
  if (board->data[j][i] != 0) {
    for (jj = j; jj < board->h && board->data[jj][i] != 0; jj++) { 
      for (ii = i; ii - i < w; ii++) {
        if (board->data[jj][ii] == 0)
          return jj - j;
      }
    }
    printf("maximum height = %d\n", jj);
  }
  return jj - j;
}

void find_largest_rect_from(
    struct board *board, 
    int i, int j, int *ei, int *ej) {
  int max_w = 0, max_h = 0, max_a = 0;
  *ei = *ej = -1;
  for (max_w = 0; max_w < board->w - i && 
      (board->data[j][i + max_w] != 0); 
      max_w++) {
    int max_aa;
    int max_hh = max_height_with_fixed_w(board, i, j, max_w + 1);
    if (max_hh > max_h) {
      max_h  = max_hh; 
    }
    max_aa = max_hh * (max_w + 1);
    printf("  area: %d x %d = %d\n", max_hh, max_w + 1, max_aa);
    if (max_aa > max_a) {
      max_a = max_aa;
      *ei = i + max_w;
      *ej = j + max_hh - 1; 
    }
  }
  printf("max width : %d\n", max_w);
  printf("max height: %d\n", max_h);
  printf("max area  : %d\n", max_a);
}

int main(int arc, char **argv) {
  struct board board;
  int jj, ii, i = 0, j = 0;
  int total_rects = 0;
  if(load_board(argv[1], &board)) return 1;
  print_board(&board);
  for (j = 0; j < board.h; j++) {
    for (i = 0; i < board.w; i++) {
      if (board.data[j][i] == 1) {
        find_largest_rect_from(&board, i, j, &ii, &jj);
        printf("largest from %d, %d ends at %d,%d\n", i, j, ii, jj);
        int marki, markj;
        total_rects++;
        for (markj = j; markj <= jj; markj++) {
          for (marki = i; marki <= ii; marki++) {
            board.data[markj][marki] = 2;
          }
        }
        print_board(&board);
      }
    }
  }
  printf("minimum %d rects are required\n", total_rects);
  return 0;
}

int load_board(char *fname, struct board *board) {
  FILE *file = fopen(fname, "r");
  int j,i;
  if (!file) return 1;
  fscanf(file, "%d %d", &board->w, &board->h);
  board->data = (int**)malloc(sizeof(int*)*board->h);
  for (j = 0; j < board->h; j++) {
    board->data[j] = (int*)malloc(sizeof(int)*board->w);
    for (i = 0; i < board->w; i++) {
      fscanf(file, "%d", &board->data[j][i]);
    }
  }
  return 0;
}

void print_board(struct board *board) {
  int i,j;
  printf("board size: %d, %d\n", board->w, board->h);
  for (j = 0; j < board->h; j++) {
    for (i = 0; i < board->w; i++) {
      printf("%d ", board->data[j][i]);
    } printf("\n");
  }
}

示例输入 1:

7 9
0 0 0 1 1 1 0
0 0 0 1 1 1 0
0 0 0 1 1 1 0
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
0 0 0 1 1 1 0
0 0 0 1 1 1 0
0 0 0 1 1 1 0

示例输入 2:

7 7
0 0 0 1 0 0 0
0 0 1 1 1 0 0
0 1 1 1 1 1 0
1 1 1 1 1 1 1
0 1 1 1 1 1 0
0 0 1 1 1 0 0
0 0 0 1 0 0 0

【讨论】:

  • 我就是这么想的!
  • 好点,但这只是技术性问题,而不是算法方面的问题。
  • @alfasin:我相信他可以使用他现有的算法进行修改。
  • 我认为这会奏效。我正在使用这个算法:stackoverflow.com/a/7497967/565635 如果我只是跳过2,我想我会找到最大的剩余矩形。
【解决方案2】:

算法的想法:

  • 只要矩阵中有1s 就可以:
  • 对于上面没有1 并且左边没有1 的每个1,请执行以下操作:
  • 去贪心:从这个1开始,沿着对角线向右和向下走,只要路上有1s - 创建一个矩形并将创建的矩形的1s更改为0s.

【讨论】:

    【解决方案3】:

    我会选择一种算法来选择点并扩展,直到它消耗所有可能的空间,然后再选择更多,直到网格上的所有点都被消耗完。

    例如,假设我们消耗 1s。

    我会选择 (0,3),最左边的 1 的最上面。我的矩形将从 0,0 开始。我会向右和向下扩展它,直到它增长到 6,2 的大小。此时,我会将这些点标记为已占用。

    然后我会选择另一个点,比如 (3,0),矩形大小为 0,0。我会一直向下扩展,直到占用最大的可用空间,大小为 2.6。

    考虑以下几点:

    0 0 0 1 0 0 0
    0 0 1 1 1 0 0
    0 1 1 1 1 1 0
    1 1 1 1 1 1 1
    0 1 1 1 1 1 0
    0 0 1 1 1 0 0
    0 0 0 1 0 0 0
    

    您可以很容易地确定,对于任何随机起点,它总是需要 4 个矩形。

    为了将点标记为“已占用”,您应该将它们标记为与标记为“不可消耗”的点不同。然后,您可以区分不可消耗的(不能扩展为)和“占用的”(可以扩展为,但不必是,因为它们已经存在)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-18
      相关资源
      最近更新 更多