首先,我们考虑搜索顺序,我们从第一列的第一格搜起,搜完当前列的方格的方格后,然后搜下一列,对于一个方格,我们优先右移,再搜左移.这样可以保证搜出来的字典序最小(因为先搜到的合法答案的字典序一定是最小的)
由于时间复杂度大概是,超时了,所以我们考虑剪枝:
1.如果一种颜色的方块只有1个或者2两个,那么就回溯
2.如果一个方块的左边有方块,则不要执行左移操作,因为这样的字典序一定不如右边的方块左移
3.一个错误的剪枝:如果左右方块一致,那么就不移动.原因是:题目要求步数要为n,有可能每次我们只走合法的路线,步数不足n次.
代码如下:
#include <bits/stdc++.h>
// #define LOCAL
#define INF 0x3f3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define int long long
#define debug(a) cerr << #a << "=" << a << endl;
using namespace std;
int n;
int cnt[12], g[5][7];
int bcnt[5][12], bg[5][5][7];
struct rec_{
int x, y, d;
}path[6];
bool st[5][7];
void mv(int a, int b, int c){
swap(g[a][b], g[c][b]);
for (;;){
bool is_change = false;
//先使用双指针处理每一列悬空的方块
for (int x = 0; x < 5; ++x){
int z = 0;
for (int y = 0; y < 7; ++y)
if (g[x][y])
g[x][z++] = g[x][y];
while (z < 7)
g[x][z++] = 0;
}
//这里使用了类似延迟删除的技巧,将所有符合删除条件的方块打上标记,后面再一起删除
memset(st, 0, sizeof st);
for (int x = 0; x < 5; ++x)
for (int y = 0; y < 7; ++y){
if (g[x][y] == 0)
continue;
int l = x, r = x;
while (l - 1 >= 0 && g[l - 1][y] == g[x][y])
--l;
while (r + 1 < 5 && g[r + 1][y] == g[x][y])
++r;
if (r - l + 1 >= 3){
st[x][y] = true;
is_change = true;
}else{
l = r = y;
while (l - 1 >= 0 && g[x][l - 1] == g[x][y])
--l;
while (r + 1 < 7 && g[x][r + 1] == g[x][y])
++r;
if (r - l + 1 >= 3){
st[x][y] = true;
is_change = true;
}
}
}
if (!is_change)
break;
for (int x = 0; x < 5; ++x)
for (int y = 0; y < 7; ++y)
if (st[x][y]){
--cnt[0];
--cnt[g[x][y]];
g[x][y] = 0;
}
}
}
bool dfs(int u){
if (u == n)
return !cnt[0];
//剪枝1:如果某个颜色的方块只有一个或者两个,那么就直接回溯
for (int i = 0; i <= 10; ++i)
if (cnt[i] == 1 || cnt[i] == 2)
return false;
//进行备份
memcpy(bg[u], g, sizeof g);
memcpy(bcnt[u], cnt, sizeof cnt);
for (int x = 0; x < 5; ++x)
for (int y = 0; y < 7; ++y){
if (g[x][y] == 0)
continue;
int nx = x + 1;//先右移
if (x + 1 < 5){
path[u] = {x, y, 1};
mv(x, y, nx);
if (dfs(u + 1))
return true;
memcpy(g, bg[u], sizeof bg[u]);
memcpy(cnt, bcnt[u], sizeof bcnt[u]);
}
nx = x - 1;//再左移,注意剪枝2:如果左边有方块,则不左移,因为左边的方块右移一定会比右边的方块左移字典序小
if (x - 1 >= 0 && g[nx][y] == 0){
path[u] = {x, y, -1};
mv(x, y, nx);
if (dfs(u + 1))
return true;
memcpy(g, bg[u], sizeof bg[u]);
memcpy(cnt, bcnt[u], sizeof bcnt[u]);
}
}
return false;
}
signed main(){
#ifdef LOCAL
freopen("input.in", "r", stdin);
freopen("output.out", "w", stdout);
#endif
IOS;
cin >> n;
for (int i = 0; i < 5; ++i){
int x = 0, t;
while (cin >> t, t){
g[i][x++] = t;
++cnt[t];
++cnt[0];
}
}
if (dfs(0))
for (int i = 0; i < n; ++i)
cout << path[i].x << " " << path[i].y << " " << path[i].d << "\n";
else
cout << -1 << "\n";
return 0;
}
这里再额外提一嘴,这道题的模拟也是有难度的,对要删除的方块打上标记,延迟删除.