目录
一,2格骨牌覆盖问题
1,剪残了的棋盘

不知道为什么,想起了KMP。。。
言归正传,既然待匹配项为1*2的长方形,那么可以把棋盘哈希到整数,分析奇偶性。
这个题目很简单,因为有个很简单的哈希函数:f=x+y,即国际象棋的棋盘。

所以答案是不能。
如果,减掉一个白格子和一个黑格子,那又能不能覆盖呢?
答案是能。
把64个格子弄成1个环

减掉2个格子之后,环变成2个线段。
因为2个格子一白一黑,所以2个线段的长度都是偶数。
所以2个线段都可以用1*2覆盖。
2,算法谜题 12 平铺多米诺问题
能否用单位长度 2*1 的多米诺牌将 8*8 的方格阵铺满? 里面不包含由两张 2*1 多米诺并行排列而成的 2*2 的正方形。
答案: 所要求的平铺方法不可能实现。
本题可用反证法证明。假设这样的平铺方法可以实现, 由于方格板是对称的, 我们假设它的左上角是被如图 4.6 所示的一块横置的多米诺牌 1 所覆盖, 那么, 第二行第一列的方格必然是被一块坚直排放的多米诺牌覆盖, 因此, 同行第二列会是一块横放的多米诺牌。按照这个思路推演下去, 我们会得到图 4.6 所示的平铺图。 在多米诺牌 13 之下放一块横置的多米诺牌, 变成了唯一的选择, 这与没有两张多米诺牌并行排列形成 2*2 的正方形的条件矛盾。

二,3格骨牌覆盖问题
1,算法谜题 78 直三格板平铺
一个直三格板是一个 3 *1 的瓦片平铺。很显然, 只要 n 能够被 3 整除, 任何人都能够通过直三格板平铺成一个 n *n 的正方形。那么, 对于一个大于 3 而又不能被 3 整除的 n 来说, 是否能够利用直三格板和一个叫做单格板的 1 * 1 瓦片平铺, 来构成一 个 n *n 的正方形?

2,L骨牌覆盖问题
边长为2^k的正方形,少了一个格子,如何用L型骨牌覆盖?
可以采用分治法:

三,4-5格骨牌覆盖问题
1,算法谜题 38 四格骨牌平铺问题


2,智力游戏 打包1


这一关,就是要把所有的木块不重叠的放进正方形。
因为正方形的边长为8,所以有64个格子。
对于木块,除了正方形之外,另外12个都有5个格子,所以一共也是64个格子。
所以最后应该是恰好铺满才对。
答案:

3,智力游戏 打包2

规则同上,但是木块不一样,这一关,木块是11个一样的木块,所以最后会有9个空格子。
答案:

4,智力游戏 打包3

5,日历拼图
https://blog.csdn.net/nameofcsdn/article/details/123765415
6,立方体拼图
代码V1:
class DancingLink
{
public:
vector<int>rows;//覆盖选中的行,值的范围是从1到m
DancingLink(int m, int n, int maxNum)
{
this->m = m, this->n = n, maxNum += n + 1;
rhead.resize(m + 1), nums.resize(n + 1);
row.resize(maxNum), col.resize(maxNum);
up.resize(maxNum), down.resize(maxNum), lef.resize(maxNum), rig.resize(maxNum);
sc.resize(m), rows.resize(m);
for (int i = 0; i <= n; i++)
{
up[i] = i, down[i] = i;
lef[i] = i - 1, rig[i] = i + 1;
row[i] = 0, col[i] = i, nums[i] = 0;
}
lef[0] = n, rig[n] = 0, nums[0] = INT_MAX;
key = n;
for (int i = 0; i <= m; i++)rhead[i] = 0;
}
void push(int r, int c)//新增坐标在(r,c)的一个节点
{
row[++key] = r, col[key] = c;
up[key] = c, down[key] = down[c];
up[down[c]] = key, down[c] = key;
if (rhead[r] == 0)rhead[r] = lef[key] = rig[key] = key;
else
{
lef[key] = rhead[r], rig[key] = rig[rhead[r]];
lef[rig[rhead[r]]] = key, rig[rhead[r]] = key;
}
nums[c]++;
}
bool dfs()
{
while (true) {
if (rig[0] == 0) {
rows.resize(rowsid);
return true;
}
int c = min_element(nums.begin(), nums.end()) - nums.begin();
del(c);
while (c = down[c]) {
if (c > n)break;
reback(col[c]);
c = sc[--scid];
rowsid--;
for (int j = rig[c]; j != c; j = rig[j])reback(col[j]);
}
sc[scid++] = c;//记录选中id
rows[rowsid++] = row[c];
for (int j = rig[c]; j != c; j = rig[j])del(col[j]);
}
return false;
}
private:
inline void del(int c)//删除第c列的所有元素和他们所在行的所有元素
{
lef[rig[c]] = lef[c], rig[lef[c]] = rig[c];
for (int i = down[c]; i != c; i = down[i])
for (int j = rig[i]; j != i; j = rig[j])
down[up[j]] = down[j], up[down[j]] = up[j], nums[col[j]]--;
nums[c] = INT_MAX;
}
inline void reback(int c)//完全回退del操作
{
lef[rig[c]] = rig[lef[c]] = c, nums[c] = 0;
for (int i = down[c]; i != c; i = down[i]) {
for (int j = rig[i]; j != i; j = rig[j])
down[up[j]] = up[down[j]] = j, nums[col[j]]++;
nums[c]++;
}
}
private:
int m, n, key;
vector<int>row, col;//每个节点的行,列
vector<int>rhead;//每行第一个节点的id
vector<int>up, down, lef, rig;//每个节点上下左右的节点id
vector<int>nums;//每一列的元素个数
vector<int>sc;
int scid = 0, rowsid = 0;
};
struct Grid
{
int r, c, h;
Grid(int rr, int cc, int hh) :r(rr), c(cc), h(hh) {}
bool operator<(const Grid& g) const
{
if (h == g.h) {
if (r == g.r)return c < g.c;
return r < g.r;
}
return h < g.h;
}
};
struct Block //一个块的所有形态
{
vector<vector<Grid>>grids;
Block(vector<vector<Grid>>g, int maxDr, int maxDc, int maxDh, const map<Grid, int>& m)//块的所有形态在最小位置包含的格子,最大偏移,待覆盖区域包含的格子
{
this->m = m;
for (auto& g2 : g) {
for (int i = 0; i < maxDr; i++)for (int j = 0; j < maxDc; j++)for (int k = 0; k < maxDh; k++) {
for (auto& x : g2)x.r += i, x.c += j, x.h += k;
if (inBoard(g2))grids.push_back(g2);
for (auto& x : g2)x.r -= i, x.c -= j, x.h -= k;;
}
}
}
Block() {}
private:
map<Grid, int>m;
bool inBoard(vector<Grid>& g)
{
for (auto& x : g)if (!m[x])return false;
return true;
}
};
vector<vector<Grid>> Cover(vector<Block>blocks, map<int, int>ids, map<Grid, int>& mg)//所有块,不限数量的块的id, 待覆盖区域包含的格子编号为1,2,3...
{
int m = 0, maxNum = 0;
for (auto& block : blocks) {
m += block.grids.size();
maxNum += block.grids.size() * (block.grids[0].size() + 1);
}
DancingLink d(m, mg.size() + blocks.size() - ids.size(), maxNum);
int r = 0, c = mg.size();
map<int, int>mrow;
for (int i = 0; i < blocks.size(); i++) {
mrow[r + 1] = i + 1;
for (auto& grids : blocks[i].grids) {
++r;
for (auto& g : grids)d.push(r, mg[g]);
if (ids[i] == 0)d.push(r, ++c);
}
}
d.dfs();
vector<int> rows = d.rows;
vector<vector<Grid>> ans;
for (auto row : rows) {
int id = 0;
while (!mrow[row])row--, id++;;
ans.push_back(blocks[mrow[row] - 1].grids[id]);
}
return ans;
}
int r, c, h, blockNum; //自定义行列数,块数
map<Grid, int> ng, mg; //ng是自定义挖掉的格子,mg是有效格子
vector<Block>blocks;//自定义每个块的所有形态在最小位置包含的格子
map<int, int>ids;
vector<Grid> rotate(vector<Grid>& g)
{
int maxRow = 0, t;
for (auto& gi : g)maxRow = max(maxRow, gi.r);
for (auto& gi : g)t = gi.c, gi.c = maxRow - gi.r, gi.r = t;
return g;
}
vector<Grid> rotate2(vector<Grid> g)
{
int maxH = 0, t;
for (auto& gi : g)maxH = max(maxH, gi.h);
for (auto& gi : g)t = gi.c, gi.c = maxH - gi.h, gi.h = t;
return g;
}
void init1()
{
r = 6, c = 6, h = 6, blockNum = 1;
ng.clear(), mg.clear();
}
void init2()
{
vector<Grid>v1 = { {0,0,0},{0,1,0},{0,2,0},{1,1,0} };
vector<Grid>v2 = { {0,0,0},{0,1,0},{0,2,0},{0,1,1} }, v3 = rotate2(v2), v4 = rotate2(v3), v5 = rotate2(v4);
blocks[0] = Block{ { v1,rotate(v1),rotate(v1),rotate(v1),v2,rotate(v2),v3,rotate(v3),v4,rotate(v4),v5,rotate(v5)}, r, c,h, mg };
ids[0] = 1;
}
void solve()
{
init1();
int id = 0;
for (int i = 0; i < r; i++)for (int j = 0; j < c; j++) for (int k = 0; k < h; k++) {
if (ng[Grid{ i, j ,k }] == 0)mg[Grid{ i, j,k }] = ++id;
}
blocks.resize(blockNum);
init2();
vector<vector<Grid>> grids = Cover(blocks, ids, mg);
vector<vector<vector<int>>>v(r);
for (int i = 0; i < r; i++) {
v[i].resize(c);
for (int j = 0; j < c; j++)v[i][j].resize(h);
}
for (int i = 0; i < grids.size(); i++) {
for (auto& g : grids[i])v[g.r][g.c][g.h] = i + 1;
}
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
for (int k = 0; k < h; k++)cout << v[i][j][k] << " ";
cout << endl;
}
cout << endl;
}
}
int main()
{
ios::sync_with_stdio(false);
clock_t start, endd;
start = clock();
freopen("D:/ans.txt", "w", stdout);
solve();
endd = clock();
double endtime = (double)(endd - start) / CLOCKS_PER_SEC;
cout << "Total time:" << endtime << endl; //s为单位
return 0;
}
输出:
1 1 1 2 2 2
7 7 7 10 10 10
20 20 20 23 23 23
21 21 21 31 31 31
24 21 37 36 31 32
3 3 3 4 4 4
5 1 8 9 2 11
13 7 9 9 10 27
25 20 35 9 23 27
28 35 35 35 49 27
24 37 37 36 32 32
24 3 39 36 4 33
5 5 8 8 11 11
13 13 34 42 42 42
25 25 34 34 42 27
28 28 34 49 49 49
24 30 37 36 48 32
26 39 39 41 33 33
5 14 8 43 44 11
13 29 43 43 43 54
25 29 29 50 54 54
28 29 50 50 50 54
30 30 40 48 48 48
26 26 39 41 41 33
14 14 14 44 44 44
16 16 16 51 51 51
18 16 17 52 51 53
18 18 40 52 52 47
18 30 40 52 47 47
26 19 40 41 38 47
6 6 6 12 12 12
15 6 17 45 12 53
15 15 17 45 45 53
15 22 17 45 46 53
22 22 22 46 46 46
19 19 19 38 38 38
Total time:0.953
对应答案:






更多解法:
参考拼接覆盖问题
四,染色技巧
1,染色的本质
染色的本质是把整个平面划分成若干个集合,根据每个部件在各个集合中的数量满足的条件,推出整体满足的数量规律。
当然,一般划分都是很有规律的。
2,4格覆盖问题
问题:用1个2*2和15个1*4可以覆盖8*8吗?
答案是不能,有2类染色法。
第一类,使得2*2为奇,1*4为偶

第二类,使得2*2为偶,1*4位奇

这2个图都强有力的说明了题目的答案是不能。
这篇博客探讨了不同尺寸的棋盘覆盖问题,包括2格、3格、4-5格骨牌的平铺策略。通过分析奇偶性和使用特定形状的骨牌,如L型骨牌,解决了一些经典算法谜题。同时,文章介绍了染色技巧在解决覆盖问题中的应用,揭示了染色的本质和不同染色方式的影响。这些问题的解答涉及到了数学的巧妙运用和逻辑推理。
《骨牌》覆盖、染色&spm=1001.2101.3001.5002&articleId=122190419&d=1&t=3&u=7d5f39460f9c4bb89c39dabd437e1359)
738

被折叠的 条评论
为什么被折叠?



