6*6俄罗斯方块方法数
这是一个我周末玩耍遇到的题目,人工俄罗斯方块。

给你一个6*6的俄罗斯版面,要求往里面填上所有的方块(所有的方块分别的形状见图片)(方块可以旋转和翻面)。使得没有重叠也没有空余缝隙。
我信心满满开始了挑战,发现是体力活。我拼了一个又一个上去,结果:我没注意到还有一个!最后一个放不上去!
我反复挑战,终于发现了一个正确答案。再后来,我又发现另一个人也完成了挑战。他和我的答案截然不同。
我便开始思考:到底有多少种不同的放置方式呢?然后又有哪些不同的放置方式呢?
于是我用编程来解答这道题:
请求出所有填的方案的方案数(任意一个位置所属方块种类不同就视为不同方案),并输出10个不同的方案。
我的思路:
首先有一个cnt数组和一个maxcnt数组。
cnt[i]表示当前第i种方块已经使用了多少个。
maxcnt[i]表示第i种方块最多使用多少个。
还有一个blk数组,表示每一个方块的不同放法
遍历每一个位置:
对于每一个位置,如果已经被方块所占用,那么直接跳转至下一个位置。
否则我会尝试每一种方块,如果还有(即cnt[i]<maxcnt[i])那么再枚举方块的各种放法,并将这次尝试先标记为true,将这个位置设为该方块的最上方一排的最左边的格子,并将整个方块都放置在这个版面上。
如果遍历方块的每一个格子时,发现出界了或着与其他方块重叠,那么将该次尝试标记为false,并直接跳转至该方块的复原操作。
如果这次尝试仍然标记为true,那么保留这次尝试并标记好该方块已使用(即cnt[i]++)继续dfs下一个位置,dfs递归回来后要将版图撤回至原本未放置该方块时的样子,还要将标记撤回(即cnt[i]--)。
代码:
#include <iostream>
#include <vector>
#include <string.h>
#define kkk 6 // 版面大小,用于debug
#define up 10 // 输出量(十万多个方案都输出并不现实)
using namespace std;
vector<vector<pair<int,int>>> blk[10];
int max_cnt[8] = { 0, 1, 2, 1, 1, 2, 1, 2 };
int cnt[8];
char grid[8][8]; // 当前版图
char grid2[8][8][8][8]; // 用于存放每个位置放置前的版图
char ch = 'a'; // 当前字符
static int cnttt = 1;
static int cntans = 0;
void print() // 输出当前版面
{
for (int r = 1; r <= kkk; r++)
{
for (int c = 1; c <= kkk; c++)
{
cout << grid[r][c] << ' ';
}
cout << endl;
}
cout << endl;
}
void dfs(int r, int c)
{
if (c > kkk) // 如果c>列数,则c = 1, r++;
{
c = 1;
r++;
}
if (r > kkk)
{
for (int i = 1; i <= kkk; i++)
{
for (int j = 1; j <= kkk; j++)
{
if (grid[i][j] == '.')
{
return;
}
}
}
cntans++;
if (cntans <= up) // 小于限量可以直接输出
{
print();
cout << endl;
}
return;
}
if (grid[r][c] != '.') // 如果已被占用,直接跳过
{
dfs(r, c + 1);
return;
}
for (int i = 1; i <= 7; i++) // 枚举每一个方块
{
if (cnt[i] >= max_cnt[i]) // 如果该方块没了,则直接跳过
{
continue;
}
memcpy(grid2[r][c], grid, sizeof(grid)); // 将当前版图存放
for (int j = 0; j < blk[i].size(); j++) // 枚举每一种放法
{
bool flag = false;
for (pair<int, int> pii : blk[i][j]) // 遍历方块的每一个位置
{
int r1 = pii.first + r;
int c1 = pii.second + c;
if (r1 < 0 || c1 < 0 || r1 > kkk || c1 > kkk || grid[r1][c1] != '.') // 如果出界或者重叠,则将flag设为false
{
flag = true;
break;
}
grid[r1][c1] = ch; // 放入
}
ch++; // 换一个字符
cnt[i]++; // 标记已使用
if (!flag)
{
dfs(r, c + 1);
}
ch--; // 换回字符
cnt[i]--; // 撤回标记
memcpy(grid, grid2[r][c], sizeof(grid2[r][c])); // 复原版图
}
}
}
int main()
{
// 方块1:凹字形
blk[1].push_back({{0,0}, {0,1}, {1,0}, {2,0}, {2,1}});
blk[1].push_back({{0,0}, {0,1}, {0,2}, {1,0}, {1,2}});
blk[1].push_back({{0,0}, {0,1}, {1,1}, {2,0}, {2,1}});
blk[1].push_back({{0,0}, {0,2}, {1,0}, {1,1}, {1,2}});
// 方块2:丁字形
blk[2].push_back({{0,0}, {1,0}, {1,1}, {2,0}});
blk[2].push_back({{0,0}, {0,1}, {0,2}, {1,1}});
blk[2].push_back({{0,0}, {1,-1}, {1,0}, {1,1}});
blk[2].push_back({{0,0}, {1,-1}, {1,0}, {2,0}});
// 方块3:弯三角
blk[3].push_back({{0,0}, {1,0}, {1,1}});
blk[3].push_back({{0,0}, {0,1}, {1,0}});
blk[3].push_back({{0,0}, {0,1}, {1,1}});
blk[3].push_back({{0,0}, {1,-1}, {1,0}});
// 方块4:L形
blk[4].push_back({{0,0}, {1,0}, {2,0}, {2,1}});
blk[4].push_back({{0,0}, {0,1}, {0,2}, {1,0}});
blk[4].push_back({{0,0}, {0,1}, {1,1}, {2,1}});
blk[4].push_back({{0,0}, {1,-2}, {1,-1}, {1,0}});
// 注意方块4还可以翻转以达到更多形状
blk[4].push_back({{0,0}, {1,0}, {2,0}, {2,-1}});
blk[4].push_back({{0,0}, {1,0}, {1,1}, {1,2}});
blk[4].push_back({{0,0}, {0,1}, {1,0}, {2,0}});
blk[4].push_back({{0,0}, {0,1}, {0,2}, {1,2}});
// 方块5:1*2
blk[5].push_back({{0,0}, {1,0}});
blk[5].push_back({{0,0}, {0,1}});
// 方块6:1*4
blk[6].push_back({{0,0}, {1,0}, {2,0}, {3,0}});
blk[6].push_back({{0,0}, {0,1}, {0,2}, {0,3}});
// 方块7:闪电形
blk[7].push_back({{0,0}, {1,-1}, {1,0}, {2,-1}});
blk[7].push_back({{0,0}, {0,1}, {1,1}, {1,2}});
// 注意方块7也可以翻转以达到更多形状
blk[7].push_back({{0,0}, {1,0}, {1,1}, {2,1}});
blk[7].push_back({{0,0}, {0,1}, {1,0}, {1,-1}});
// 初始化版图
for (int i = 1; i <= kkk; i++)
{
for (int j = 1; j <= kkk; j++)
{
grid[i][j] = '.';
}
}
dfs(1, 1);
cout << "当前输出" << min(up, cntans) << "种, 一共" << cntans << "种" << endl;
return 0;
}
不足:
1、输出时不同方块仅由不同字符区分,没有作颜色区分
2、运行时长 两年半 10秒多,暂时想不到优化。。。
那么这道“题”大概就是这样,也感谢各位宇宙超级无敌巨巨佬能够耐心阅读完这篇宇宙最菜小蒟蒻的文章,如有什么可以帮助小蒟蒻改进不足的方法,也请各位大神指导一下小蒟蒻,Thank you。
点个赞呗

浙公网安备 33010602011771号