专栏文章
题解:P1312 [NOIP2011 提高组] Mayan 游戏
P1312题解参与者 8已保存评论 9
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 9 条
- 当前快照
- 1 份
- 快照标识符
- @miqel9rx
- 此快照首次捕获于
- 2025/12/04 03:31 3 个月前
- 此快照最后确认于
- 2025/12/04 03:31 3 个月前
题目分析
本题没有什么特别的办法,只能尝试每一个步骤的走法。说到尝试,就一定是搜索了。不过本题的搜索比较复杂,夹杂了模拟。我认为本题难度主要在代码上。
为了方便,我们将本题的编号起点 调整为 ,并在最后输出时 。
移动-move 函数
先不管如何搜索操作步骤。对于每一次移动块,先要交换块,然后进行删除,删除后出现新的空,需要进行下落,下落后又有新的块……由此,我们可以先来实现一个移动的函数。
CPPvoid move(int x, int y, int k){
swap(mp[x][y], mp[x+k][y]); // 1交换
down(); // 2下落
while (remove()) down(); // 3判定并继续下落
}
其中
remove 表示消除(返回值为是否进行了至少一组的消除),down 表示下落。下落-down 函数
接下来我们就可以开始完成这两个函数了。先来看
down 函数。首先我们知道,下落不会引起列交换。所以我们对于 列,分别处理。快速的办法就是新建一个数组,将所有不为 的块拷贝进去(不留空白),再重新拷贝出来。例如一列 ,进入数组后 ,再回到这一列就变成了 ,比朴素向下找方便。
CPPvoid down(){
for (int i = 1; i <= 5; i++){
int b[10] = {}, cnt = 0; // 此处将b数组清0了
for (int j = 1; j <= 7; j++){
if (mp[i][j]) b[++cnt] = mp[i][j]; // 加入新数组
}
for (int j = 1; j <= 7; j++){
mp[i][j] = b[j]; // 移回原数组,并覆盖多余部分
}
}
}
移除-remove 函数
接下来就是
remove 函数。这是比较复杂的一个函数。因为可能出现题目描述中“共享方块”的问题,我们不能直接修改原数组。这个时候我们就需要一个辅助数组了。一旦找到某个块可以作为消除块的中间,标记到辅助数组。最终把辅助数组中的标记和原数组中的被标记位置一同清空,并检查是否消除了块。一定注意,空白不能作为一个“空白块”的中间位置,遇到空白要跳过,否则可能会出现死循环。
CPPbool remove(){
for (int i = 1; i <= 5; i++){
for (int j = 1; j <= 7; j++){
if (!mp[i][j]) continue; // 空白块要跳过
if (mp[i-1][j] == mp[i][j] && mp[i+1][j] == mp[i][j])
v[i-1][j] = v[i][j] = v[i+1][j] = 1; // 标记横向块
if (mp[i][j-1] == mp[i][j] && mp[i][j+1] == mp[i][j])
v[i][j-1] = v[i][j] = v[i][j+1] = 1; // 标记纵向块
}
}
bool ret = false; // 寻找是否消除
for (int i = 1; i <= 5; i++){
for (int j = 1; j <= 7; j++){
if (v[i][j]) {
mp[i][j] = v[i][j] = 0;
ret = true;
}
}
}
return ret;
}
三个函数都完成了,接下来就可以进入重点的搜索部分了。
搜索-dfs 函数
要说搜索,我们可以枚举 个块判断消除,这是完全没有问题的。最坑的地方就是回溯,因为无法还原出原状态。
当然,解决办法也很简单——再加辅助数组。在找之前,先把原数据复制过来,每次回溯再复制回去。
另外,除了剪枝,我们还得使用数组记录答案,最终还需要一个判断是否完成的函数
CPPcheck,具体怎么写见下一部分。void dfs(int x){
if (flag) return ;
if (check()){
for (int i = 1; i <= n; i++){
cout << ans[i][1] << " " << ans[i][2] << " " << ans[i][3] << endl;
}
flag = true;
return ;
}
if (x > n) return ;
int tmp[10][10];
memcpy(tmp, mp, sizeof tmp);
for (int i = 1; i <= 5; i++){
for (int j = 1; j <= 7; j++){
if (!mp[i][j]) break;
if (i < 5){
ans[x][1] = i - 1;
ans[x][2] = j - 1;
ans[x][3] = 1;
move(i, j, 1); dfs(x+1);
memcpy(mp, tmp, sizeof mp);
}
if (i > 1 && !mp[i-1][j]){
ans[x][1] = i - 1;
ans[x][2] = j - 1;
ans[x][3] = -1;
move(i, j, -1); dfs(x+1);
memcpy(mp, tmp, sizeof mp);
}
}
}
}
注意: 优先于 ,一定要先搜索向右移动。向右移动,右边只要不为空,即可移动。向左移动需要注意不能移到空白处。
检查-check 函数
非常简单,由于我们可以保证在执行
CPPcheck 函数时,数组状态完全合法,所以只找第一横行是否有块即可。bool check(){
for (int i = 1; i <= 5; i++){
if (mp[i][1]) return false;
}
return true;
}
关于 Hack 数据
Hack 数据仅需 步即可完成,但它的输入 大于 。我的代码最开始被 Hack 了,是因为判断向右的时候多加了不相同才换的条件。对于这种情况,需要通过“无效交换”“拖延”步数,所以不能加“不相同才换”的条件。
题目总结与参考代码
本题主要难度是如何进行搜索,以及搜索过程中的模拟。调代码的难度也是比较大的,可以通过加辅助输出或者
CPPreturn 来调错。#include <bits/stdc++.h>
using namespace std;
int n, mp[10][10], v[10][10], ans[10][5];
bool flag;
void down(){
for (int i = 1; i <= 5; i++){
int b[10] = {}, cnt = 0;
for (int j = 1; j <= 7; j++){
if (mp[i][j]) b[++cnt] = mp[i][j];
}
for (int j = 1; j <= 7; j++){
mp[i][j] = b[j];
}
}
}
bool remove(){
for (int i = 1; i <= 5; i++){
for (int j = 1; j <= 7; j++){
if (!mp[i][j]) continue;
if (mp[i-1][j] == mp[i][j] && mp[i+1][j] == mp[i][j])
v[i-1][j] = v[i][j] = v[i+1][j] = 1;
if (mp[i][j-1] == mp[i][j] && mp[i][j+1] == mp[i][j])
v[i][j-1] = v[i][j] = v[i][j+1] = 1;
}
}
bool ret = false;
for (int i = 1; i <= 5; i++){
for (int j = 1; j <= 7; j++){
if (v[i][j]) {
mp[i][j] = v[i][j] = 0;
ret = true;
}
}
}
return ret;
}
bool check(){
for (int i = 1; i <= 5; i++){
if (mp[i][1]) return false;
}
return true;
}
void move(int x, int y, int k){
swap(mp[x][y], mp[x+k][y]);
down();
while (remove()) down();
}
void dfs(int x){
if (flag) return ;
if (check()){
for (int i = 1; i <= n; i++){
cout << ans[i][1] << " " << ans[i][2] << " " << ans[i][3] << endl;
}
flag = true;
return ;
}
if (x > n) return ;
int tmp[10][10];
memcpy(tmp, mp, sizeof tmp);
for (int i = 1; i <= 5; i++){
for (int j = 1; j <= 7; j++){
if (!mp[i][j]) break;
if (i < 5){
ans[x][1] = i - 1;
ans[x][2] = j - 1;
ans[x][3] = 1;
move(i, j, 1); dfs(x+1);
memcpy(mp, tmp, sizeof mp);
}
if (i > 1 && !mp[i-1][j]){
ans[x][1] = i - 1;
ans[x][2] = j - 1;
ans[x][3] = -1;
move(i, j, -1); dfs(x+1);
memcpy(mp, tmp, sizeof mp);
}
}
}
}
int main(){
cin >> n;
for (int i = 1; i <= 5; i++){
int p = 0, x; cin >> x;
while (x) {
mp[i][++p] = x;
cin >> x;
}
}
dfs(1);
if (!flag)
cout << -1 << endl;
return 0;
}
相关推荐
评论
共 9 条评论,欢迎与作者交流。
正在加载评论...