小米笔试真题 - 摆积木
摆积木
题目描述
小钟在一张长为n、宽为m的网格图上摆放积木。图上有一些格子是特殊格子,小钟有无限个尺寸为1×3或3×1的积木。每次摆放积木需满足以下条件:
积木至少有一端与特殊格子重合;
只能在网格图内摆放,不能超出;
当前要摆放的积木不能与已摆放的积木重叠。
小钟会一直按上述要求摆放积木,直到无法再摆放为止。小爱想知道网格图最终有多少种可能的局面,两种局面不同,当且仅当网格图中存在某个格子,一种局面中该格子被积木覆盖,另一种局面中没有被覆盖。
1≤n, m≤8,特殊格子数量不超过13,数据随机生成。
输入描述
第一行有两个整数n(1≤n≤8)、m(1≤m≤8),分别表示网格图的长和宽。
接下来n行,每行输入m个字符:
若网格图第i行第j列位置是特殊格子,则字符为*;
否则为.。
输出描述
输出一个整数,表示网格图最终可能的局面个数。
示例1
输入
4 4
....
...*
.*..
....
输出
2
解释
样例解释
对于第一个样例,两种可能的游戏结束局面如下:
.*..
.*.*
.*.*
...*....
.***
.***
....
思路和代码
使用递归回溯枚举所有合法方案。具体优化点如下:
- 可以将摆放方案转换为
无符号64位整数表示,用于快速去重。 - 可以集合中存储中间出现过的状态,减少重复搜索,剪枝。
- 可以使用集合存储所有最终状态,用于对最终摆放结果去重。
#include<iostream>
#include<vector>
#include<string>
#include <utility>
#include<algorithm>
#include<map>
#include<unordered_set>
#include<cstring>
#include <cstdint>
using namespace std;
const int N = 8;
int n,m;
bool special[N][N];
// 记录使用装填
bool used[N][N];
// 最终结果去重
unordered_set<uint64_t> s;
// 用于去重,剪枝
unordered_set<uint64_t> visited;// 将摆放装填转换为数字表示
uint64_t encode() {uint64_t mask = 0;int index = 0;for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++,index++) {if (used[i][j]) {mask |= (1ULL << index);}}}return mask;
}// 检查指定三个位置是否可以存放
bool canPlay(int x1, int y1, int x2, int y2, int x3, int y3) {// 越界int minX = min({x1, x2, x3});int maxX = max({x1,x2,x3});if (minX < 0 || maxX >= n) {return false;}int minY = min({y1,y2,y3});int maxY = max({y1,y2,y3});if (minY < 0 || maxY >= m) {return false;}// 重叠if (used[x1][y1] || used[x2][y2] || used[x3][y3] ) {return false;}// 至少一个端点为特殊个子if (special[x1][y1] || special[x3][y3]) {return true;}return false;
}void dfs() {uint64_t mask = encode();if (visited.count(mask)) {return;}visited.insert(mask);bool can_place = false;for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {// 横着放 i,j为左端点if (canPlay(i, j, i,j+1, i,j + 2)) {used[i][j] = used[i][j+1]=used[i][j+2]=true;dfs();used[i][j] = used[i][j+1]=used[i][j+2]=false;can_place = true;}// 横着放 i,j为右端点if (canPlay(i, j, i,j-1, i,j -2)) {used[i][j] = used[i][j-1]=used[i][j-2]=true;dfs();used[i][j] = used[i][j-1]=used[i][j-2]=false;can_place = true;}// 竖着放 i,j为上端点if (canPlay(i, j, i+1,j, i+2,j)) {used[i][j] = used[i+1][j]=used[i+2][j]=true;dfs();used[i][j] = used[i+1][j]=used[i+2][j]=false;can_place = true;} // 竖着放 i,j为下端点if (canPlay(i, j, i-1,j, i-2,j)) {used[i][j] = used[i-1][j]=used[i-2][j]=true;dfs();used[i][j] = used[i-1][j]=used[i-2][j]=false;can_place = true;} }}if (!can_place) {s.insert(mask);}
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);memset(special, 0, sizeof(special));cin >> n >> m;for (int i = 0; i < n; i++) {string row;cin >> row;// 记录特殊方格位置for (int j = 0; j < m; j++) {special[i][j] = (row[j] == '*');used[i][j] = false;}}dfs();cout << s.size();return 0;
}
