题目
Overlapping Squares - UVA 12113 - Virtual Judge
这道题要我们在一个4x4大小的grid中放入2x2大小的正方形,问放1-6个正方形能不能形成给定图形。
因为要画边和中间,所以一个1X1正方形占2x3的格子:
123
1 _
2|_|
如果竖边不画,也要用空格,所以2x2的正方形要占3X5个格子。如下图:
12345
1 _ _
2|_|_|
3|_|_|
1 _ _
2| |
3|_ _|
所以整个图 (4x4)要占 5x9个格子。
INPUT
每个case有5行,每一行前九个字符是图,最后一个字符是#。
首字符如果是0就结束。
#
_ _ _ #
| |_ _| #
|_| | #
|_ _| #
#
_ _ #
| | #
|_ _| #
#
_ _ _ _ #
|_|_|_|_|#
|_|_|_|_|#
|_|_|_|_|#
|_|_|_|_|#
_ _ #
_| |_ #
| |_ _| |#
|_| |_|#
|_ _|_|#
0
OUTPUT
Case 1: Yes
Case 2: Yes
Case 3: No
Case 4: Yes
分析
因为图形有遮挡,最后放上去的正方形肯定是没遮挡的。所以我们可以先找最后放的正方形,然后再去找之前放的有遮挡的正方形。如果发现现在的位置已经放过正方形了,那么我们就不用再在这个地方回溯了。
整张图有5X9的大小,每次放进去的正方形大小是3X5,横边每次要移动两格,竖边每次一格。所以搜索一次全图需要((9 - 5)/ 2 + 1)x(5 - 3 + 1)= 9 次。
每次搜索都要把3X5的正方形加在原图上,总共需要3x5=15次操作,然后回溯,也需要15次。所以总共需要30次操作
因此,回溯一个正方形需要的time complexity 是O(30*9) = O(270)
当然,我们可以先检查这个位置的图形和要添加的正方形能不能对的上,对不上的话就不用添加正方形了。这样的话大部分的位置只需要检查一次正方形的大小就可以了。
这样的话,大部分的位置只需要O(15*9) = O(135)的时间就可以了
解答树
如果每种状态都要搜索,我们最多需要搜索 9^6 (531441)种状态。总共搜索完需要 531441 X 270 ~= 1.435e9的时间。
然而实际情况比这个数字会小很多。
因为我们只有在找到正方形的情况下才可能往下一层搜索,没找到的情况就可以剪枝。
第一层最多可以出现4种情况
第二层最多可以出现8种情况 (只有第一层只有一种且在中间的时候会有8种)
…
只要找到我们就不用再往下回溯了。所以这样看的话,可以肯定直接回溯的速度就可以足够把这道题目解出来。
Edge case
注意在添加正方形的时候,最上边那行的空格可以是’|’。 比如下边的Input:
#
_ _ _ #
| |_ _| #
|_| | #
|_ _| #
里边的正方形是这样的,这样的图形也是正确的:
|_ _|
| |
|_ _|
所以在搜索的时候可以直接把(0,0),(0,2),(0,4)这三个坐标跳过。
AC 代码
我的解法里没有真正去画正方形,而是检查正确了直接把检查过的位置加一个mask,在之后的检查中直接跳过检查过的位置。同时记录 `_` 和 `|` 的数量,当添加的`_`和`|`数量和原图相等的时候就证明找到完全一样的图形了。
#include <bits/stdc++.h>
using namespace std;
const int W = 9, H = 5, SW = 5, SH = 3;
const char SQUARE [SH][SW] = {
{'*', '_', '*', '_', '*'},
{'|', ' ', ' ', ' ', '|'},
{'|', '_', ' ', '_', '|'},
};
char grid [H][W];
bool mask [H][W], sqMask[SH][SW];
int cnt[2]; // 0: _, 1: |
bool check(int i, int j) {
bool valid = false;
for (int r = i; r < i + SH; r++)
for(int c = j; c < j + SW; c++) {
int si = r - i, sj = c - j;
if (mask[r][c]) sqMask[si][sj] = 0;
else if (SQUARE[si][sj] == '*') continue;
else {
if (grid[r][c] != SQUARE[si][sj]) return false;
valid = sqMask[si][sj] = 1;
}
}
return valid;
}
void addRemove(int i, int j, bool isAdd) {
for (int r = i; r < i + SH; r++)
for(int c = j; c < j + SW; c++) {
int si = r - i, sj = c - j;
if (sqMask[si][sj]) {
mask[r][c] = isAdd ? 1 : 0;
if (SQUARE[si][sj] == '_') cnt[0] += isAdd ? -1 : 1 ;
if (SQUARE[si][sj] == '|') cnt[1] += isAdd ? -1 : 1;
}
}
}
bool bt(int d) {
if (d > 6) return false;
for (int i = 0; i <= (H - SH); i++ ) {
for (int j = 0; j <= W - SW; j+= 2) {
bool ans = check(i, j);
if(!ans) continue;
addRemove(i, j, true);
if (!cnt[0] && !cnt[1]) return true;
if (bt(d + 1)) return true;
addRemove(i, j, false);
}
}
return false;
}
int main() {
int kase = 1; char fc;
for(;;) {
cnt[0] = cnt[1] = 0;
fc = getchar();
if (fc == '0') break;
memset(mask, 0, sizeof(mask));
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 9; j++) {
if (i == 0 && j == 0) grid[i][j] = fc;
else grid[i][j] = getchar();
if (grid[i][j] == '_') cnt[0]++;
if (grid[i][j] == '|') cnt[1]++;
}
getchar();getchar(); // skip # and \n
}
bool ans = bt(1);
cout << "Case " << kase++ << ": " << (ans ? "Yes" : "No") << endl;
}
return 0;
}