说我有以下二进制矩阵:
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
&s,将该矩形添加到集合中,然后将所有1's
标记为0
& #39; s,直到所有1
消失。虽然这个集合将涵盖每1个而不是单个0
,但它不一定具有最小的基数(此算法将在上面的示例中失败)。
答案 0 :(得分:4)
我认为你应该用2代替0而不是0代替覆盖1。这样你就可以在覆盖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
答案 1 :(得分:0)
算法的想法:
1
s:1
以上没有1
并且左侧没有1
的人,请执行以下操作:1
开始,向右和向下对角线,只要路上有1
- 创建一个矩形并更改1
将创建的矩形的s放入0
s。答案 2 :(得分:0)
我会选择一种算法来挑选点并进行扩展,直到它产生所有可能的空间,然后选择更多,直到网格上的所有点都被消耗掉。
对于你的例子,假设我们消耗了1s。
我会选择(0,3),最左边的1s的最上面。我的矩形的起始大小为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个矩形。
为了将点标记为“已占用”,您应该将它们标记为不同于标记为“不可消耗”的点。然后,您可以区分不可消耗的(不能扩展到)和“占用”(可以扩展到,但不一定是因为它们已经存在)。