参考自蓝桥杯国赛特训营
题意
有一个 3×10 的空地,往里面铺 1×2 的瓷砖,瓷砖有两种颜色(橙色和黄色),并且任意一个 2×2 的空地的瓷砖颜色不能相同,问共有几种贴法。
分析
数据范围很小,只用铺下 3 * 10 / 2 = 15
块瓷砖,所以可以枚举所有瓷砖得摆放方式以及颜色。
用深搜枚举每块瓷砖的颜色,当所有瓷砖都贴完并且合法时,记录答案。
空地状态 g[x] [y]:-1
表示未贴瓷砖,0 表示橙色,1 表示黄色。
当dfs枚举到任意一块没有上色的空地时,有两种贴瓷砖方法(横向和纵向),每种方法有两种颜色
因为只有 30 块瓷砖,每块瓷砖有两种颜色可选,所以最多有 2^30 种情况,刚好可以用一个 int 表达。所以用 status 代表涂色方案,其二进制的第 i 位,代表了第 i 块瓷砖的颜色。之后可以用 set 容器完成去重工作。(个人感悟:对于这种需要存储状态并进行去重的题目,如果每个位置只有两种状态,可以考虑使用一个整数或者一个字符串来代表一个状态, 如果每个位置有多个选择,则只能虑使用一个字符串来代表一个状态)
#include<bits/stdc++.h>
using namespace std;
using LL =long long;
int g[4][11] = {0}; //g[x][y] = -1表示未放置,=0表示放置橙色,=1表示放置黄色
set<string> st; //存放方案数,用于去重
bool check() //对所有放置结果进行检查,判断放置是否合法
{
for(int i = 1; i <= 2; ++i)
{
for(int j = 1; j <= 9; ++j)
{
int temp = g[i][j] + g[i+1][j] + g[i][j+1] + g[i+1][j+1];
if(temp % 4 == 0) //四个格子都放置的是黄色为0,都放置的是橙色为4
return false; //合法为任意2x2的翻个贴的颜色不一样
}
}
return true;
}
void dfs(int x, int y) //开始从x和y放置瓷砖
{
if(x > 3 )
{
if(check())
{
string s = "";
for(int i =1; i <= 3; ++i)
{
for(int j = 1; j <= 10; ++j)
{
s += g[i][j] - '0';
}
}
st.insert(s);
}
return ;
}
if(g[x][y] == -1)
{
if( y < 10 && g[x][y+1] == -1) //横向放置
{
for(int i = 0; i <= 1; ++i) //选择颜色
{
g[x][y] = g[x][y+1] = i;
if(y +2 <= 10)
dfs(x, y+2); //预剪枝,对于终止条件的处理更方便
else
dfs(x+1, 1);
//回溯
g[x][y] = g[x][y+1] = -1;
}
}
if(x < 3 && g[x+1][y] == -1) // 纵向放置
{
for(int i = 0; i <=1; ++i) //选择颜色
{
g[x][y] = g[x+1][y] = i;
if( y +1 <= 10 )
dfs(x, y+1);
else
dfs(x+1, 1);
//回溯
g[x][y] = g[x+1][y] = -1;
}
}
}
else if( y == 10)
dfs(x+1, 1); //到下一行的第一列
else
dfs(x, y+1); //同一行的下一列
}
int main()
{
memset(g, -1, sizeof(g));
dfs(1,1);
cout << st.size() << endl;
return 0;
}