这个是某夕夕上最近很火的一款益智小游戏,版面设计看起来也很nice!那么类似这种游戏对于程序员的正确打开方式还是写个程序解决,并没有什么益智不益智,因为说实话拼这个东西还是太费力了。
那么怎么解决这个问题呢?很明显先把盘子还有各个小块的形状记录起来,盘子用一个全局数组,而小块则用一个结构体node,还要存储长宽,并且有旋转等函数,然后采用深度优先搜索算法(DFS,这是一个很基础的算法,还没了解的应该先了解),注意回溯,然后就能罗列所有的情况,这个(右边的)差不多是7*7大小的,并且总共有8个小块,那么每个小块大概有7*7*8种放置的方式(加上旋转和翻转,一开始没想到翻转搞多了很多时间),因此计算量(粗略的说法,放置一块的意思,包括判断、更新数组等等操作)就最多是(7*7*8)^8次(大概五万亿亿次,粗略算1亿次1秒,但是加上很多不可能的情况(判断一下就过了)还有剪枝(去掉了极多的情况),还有这个是最大估计,其实远小于五万亿秒,本人跑了半个多小时,虽说还是不少)。再加上适当判断,排序,存文件等等,我们就能愉快的完成任务了!话不多说,代码如下:
#include<iostream>
#include<cstring>
#include<fstream>
#include<map>
#include<algorithm>
using namespace std;
const int ma[12] = { 31,29,31,30,31,30,31,31,30,31,30,31 };
int day_num = 0;
int pan[10][10];
struct node {
node()
{
memset(arr, 0, sizeof arr);
}
int n, m;
int c;
bool st[2][4];//哪几次旋转是没用的
bool arr[10][10];
void rotate()//有些可能转了等于没转,所以要四种状态都存起来
{
bool tmp[10][10];
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
tmp[j][n - 1 - i] = arr[i][j];
}
}
memcpy(arr, tmp, sizeof arr);
swap(n, m);
}
void flip()
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m / 2; j++)//要不然白换了
{
swap(arr[i][j], arr[i][m - 1 - j]);
}
}
}
void judge()
{
memset(st, 0, sizeof st);
bool tmp[8][10][10];
memset(tmp, 0, sizeof tmp);
int cnt = 0;
for (int t = 0; t < 2; t++)
{
for (int i = 0; i < 4; i++)
{
bool FLAG = 1;
for (int j = 0; j < cnt; j++)
{
bool flag = 0;
for (int ii = 0; ii < n; ii++)
{
for (int jj = 0; jj < m; jj++)
{
if (tmp[j][ii][jj] != arr[ii][jj])
{
flag = 1;
break;
}
}
if (flag)break;
}
if (!flag)//完全相同
{
FLAG = 0;
st[t][i] = 1;//第i次循环的状态跟前面重复了
}
}
if (FLAG)
{
for (int ii = 0; ii < n; ii++)
{
for (int jj = 0; jj < m; jj++)
{
tmp[cnt][ii][jj] = arr[ii][jj];
}
}
cnt++;
}
rotate();
}
flip();
}
}
void pt(int x, int y, bool f = 1)//从x,y开始
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
int xx = x + i, yy = y + j;
if (arr[i][j])
{
if (f)pan[xx][yy] = c;
else pan[xx][yy] = 0;
}
}
}
}
}nd[8];
inline void printPan(ofstream &ofs, int pan[][10])
{
for (int i = 0; i < 7; i++)
{
for (int j = 0; j < 7; j++)
{
if (pan[i][j] != -1)ofs << pan[i][j] << " ";
else ofs << " ";
}
ofs << "\n";
}
ofs << "\n";
}
void prework()
{
for (int i = 0; i < 12; i++) day_num += ma[i];
for (int i = 0; i < 7; i++)
{
for (int j = 0; j < 7; j++)
{
pan[i][j] = 0;
}
}
pan[0][6] = pan[1][6] = pan[6][3] = pan[6][4] = pan[6][5] = pan[6][6] = -1;
for (int i = 0; i < 8; i++)nd[i].c = i + 1;
nd[0].n = 2, nd[0].m = 3;
for (int i = 0; i < 2; i++)for (int j = 0; j < 3; j++)nd[0].arr[i][j] = 1;
nd[1].n = 3, nd[1].m = 3;
for (int i = 0; i < 3; i++)for (int j = 0; j < 3; j++)nd[1].arr[i][j] = 1;
for (int i = 1; i < 3; i++)for (int j = 0; j < 2; j++)nd[1].arr[i][j] = 0;
nd[2].n = 4, nd[2].m = 2;
for (int i = 0; i < 4; i++)for (int j = 0; j < 2; j++)nd[2].arr[i][j] = 1;
nd[2].arr[0][1] = nd[2].arr[2][1] = nd[2].arr[3][1] = 0;
nd[3].n = 3, nd[3].m = 2;
for (int i = 0; i < 3; i++)for (int j = 0; j < 2; j++)nd[3].arr[i][j] = 1;
nd[3].arr[1][0] = 0;
nd[4].n = 2, nd[4].m = 3;
for (int i = 0; i < 2; i++)for (int j = 0; j < 3; j++)nd[4].arr[i][j] = 1;
nd[4].arr[0][0] = 0;
nd[5].n = 4, nd[5].m = 2;
for (int i = 0; i < 4; i++)for (int j = 0; j < 2; j++)nd[5].arr[i][j] = 1;
nd[5].arr[0][0] = nd[5].arr[2][1] = nd[5].arr[3][1] = 0;
nd[6].n = 3, nd[6].m = 3;
for (int i = 0; i < 3; i++)for (int j = 0; j < 3; j++)nd[6].arr[i][j] = 1;
nd[6].arr[0][0] = nd[6].arr[0][1] = nd[6].arr[2][1] = nd[6].arr[2][2] = 0;
nd[7].n = 2, nd[7].m = 4;
for (int j = 0; j < 4; j++)nd[7].arr[1][j] = 1;
nd[7].arr[0][3] = 1;
for (int i = 0; i < 8; i++)nd[i].judge();
}
inline bool check(int x, int y)
{
return x >= 0 && x < 7 && y >= 0 && y < 7 && pan[x][y] == 0;
}
struct date {
int m, d;
int arr[10][10];
bool operator<(const date &a)const
{
return m < a.m || m == a.m&&d < a.d;
}
}ans[400];
map<int, bool>mp;
int _cnt = 0;
ofstream ofs("答案.txt");
inline void printMpSize()
{
int cnt = 0;
for (map<int, bool>::iterator it = mp.begin(); it != mp.end(); it++)
{
if (it->second)cnt++;
}
cout << cnt << "\n";
}
void save()
{
printMpSize();
int id[2][2] = { -1 };
for (int i = 0; i < 7; i++)
{
for (int j = 0; j < 7; j++)
{
if (!pan[i][j])
{
if (id[0][0] == -1)
{
id[0][0] = i;
id[0][1] = j;
}
else
{
id[1][0] = i;
id[1][1] = j;
}
}
}
}
if ((id[0][0] - 1.5)*(id[1][0] - 1.5) > 0) return;//都不是或都是月份
int m, d;
if (id[0][0] < 2)m = id[0][0] * 6 + id[0][1], d = (id[1][0] - 2) * 7 + id[1][1];
else m = id[1][0] * 6 + id[1][1], d = (id[0][0] - 2) * 7 + id[0][1];
if (d >= ma[m])return;
if (mp[m * 100 + d])return;
mp[m * 100 + d] = 1;
ans[_cnt].m = m + 1;
ans[_cnt].d = d + 1;
memcpy(ans[_cnt].arr, pan, sizeof pan);
_cnt++;
}
void dfs(int x)//第x个
{
if (_cnt >= day_num)return;
for (int i = 0; i < 7; i++)
{
for (int j = 0; j < 7; j++)
{
for (int fl = 0; fl < 2; fl++)
{
for (int r = 0; r < 4; r++)//旋转
{
if (nd[x].st[fl][r])
{
nd[x].rotate();
continue;
}
bool flag = 0;
int n = nd[x].n, m = nd[x].m;
for (int ii = 0; ii < n; ii++)
{
for (int jj = 0; jj < m; jj++)
{
int xx = i + ii, yy = j + jj;
if (nd[x].arr[ii][jj] && !check(xx, yy))
{
flag = 1;
break;
}
}
if (flag)break;
}
if (!flag)//flag为0,可以装,装完继续搜
{
nd[x].pt(i, j);
if (x == 7)
{
save();
}
else
{
dfs(x + 1);
}
nd[x].pt(i, j, 0);
}
nd[x].rotate();
}
nd[x].flip();
}
}
}
}
int main()
{
prework();
dfs(0);
sort(ans, ans + _cnt);
for (int i = 0; i < _cnt; i++)
{
date &a = ans[i];
ofs << a.m << "月" << a.d << "日" << "\n";
printPan(ofs, a.arr);
}
ofs.close();
return 0;
}
至于这个游戏的合理性证明我也是想不通,真是非常其妙,结果真的跑出每个日期的拼法。下面展示两个日期:
8月19日
4 4 1 1 1 2
4 0 1 1 1 2
4 4 7 2 2 2 3
7 7 7 6 6 6 3
7 5 6 6 0 3 3
5 5 8 8 8 8 3
5 5 8
10月1日
1 1 1 2 2 2
1 1 1 0 6 2
0 4 4 4 6 2 8
3 4 7 4 6 6 8
3 3 7 7 7 6 8
3 5 5 5 7 8 8
3 5 5
另外,对于完全不懂编程或者不懂算法或者不想花半个小时运行程序的话,这里提供了直接下载答案的地址:答案。点集红色箭头直指向的地方就能下载,下载后的那些问号是日和月,并不会乱码。