题目链接:Cubic Eight-Puzzle
题目描述:
给定一个初始状态和一个终止状态的 3 ∗ 3 3*3 3∗3网格,网格中含有 8 8 8个立方体和一个空白,每个立方体的颜色如下:
需要注意的是:只会给出终止状态的立方体的顶面朝向,而初始的立方体摆放方式如下图,其中空格位置由输入的两个整数决定:
你每次操作可以将初始状态的一个立方体推入到空白地方,推入过程中会发生旋转,过程如下图所示:
问能否 30 30 30步以内到达目标状态。
题解:
本题的思路很简单,我们可以使用双向 B F S BFS BFS,每个非空格方格有六种状态(分别对应六个面朝上),所以我们可以用一个四位的二进制来表示一共有九个网格所以需要 36 36 36位二进制来表示一种状态。由于目标状态的侧面不确定,所以每个侧面对应两种情况,我们需要初始将所有可能的目标状态放入队列中,然后从起始状态进行搜索即可,需要注意的是,双向 B F S BFS BFS应该每次选择较小的队列进行搜索,搜索的次数为选择队列当前的大小,而不是一个队列搜索到头。
这道题目的细节比较多,推旋转后的状态一定要仔细。
同时这道题用 S T L STL STL的话常数较大,很容易超时,需要注意。
代码:
#include <bits/stdc++.h>
const int MAXN = 9;
using namespace std;
typedef pair<int, long long> State;
int x, y, ans;
long long endStatus, initStatus;
long long status[MAXN];
string s[MAXN];
queue<State> q[2];
unordered_map<long long, int> inQ, dis;
void getStatus(string &s, vector<int> &status)
{
if (s[0] == 'E') {
status.push_back(0);
} else if (s[0] == 'W') {
status.push_back(1);
status.push_back(2);
} else if (s[0] == 'R') {
status.push_back(3);
status.push_back(4);
} else if (s[0] == 'B') {
status.push_back(5);
status.push_back(6);
}
}
void initEndStatus(int p)
{
if (p == 9) {
endStatus = 0;
for (int i = 0; i < 9; i++) {
endStatus |= status[i] << (4 * i);
}
inQ[endStatus] = 1;
dis[endStatus] = 0;
q[1].push({
0, endStatus});
return;
}
vector<int> v;
getStatus(s[p], v);
for (int nowStatus : v) {
status[p] = nowStatus;
initEndStatus(p + 1);
}
}
long long getInitStatus()
{
long long initStatus = 0;
for (int i = 0; i < 9; i++) {
if (i / 3 == x && i % 3== y) {
continue; }
initStatus |= 1LL << (4 * i);
}
return initStatus;
}
void init()
{
while (!q[0].empty()) {
q[0].pop(); }
while (!q[1].empty()) {
q[1].pop(); }
inQ.clear();
dis.clear();
initStatus = getInitStatus();
initEndStatus(0);
}
void getEmptyPos(long long status, int &x, int &y)
{
for (int i = 0; i < 9; i++) {
if (((status >> (4 * i)) & (0b1111)) == 0) {
x = i / 3;
y = i % 3;
break;
}
}
}
// 获取x, y位置的状态
long long getStatus(long long status, int x, int y)
{
return (status >> (4 * (3 * x + y))) & 0b1111;
}
int dx[] = {
0, 0, 1, -1};
int dy[] = {
1, -1, 0, 0};
long long p[7][4] = {
{
0, 0, 0, 0},
{
5, 5, 3, 3},
{
4, 4, 6, 6},
{
6, 6, 1, 1},
{
2, 2, 5, 5},
{
1, 1, 4, 4},
{
3, 3, 2, 2}
};
long long transfer(long long status, int i)
{
int x = 0, y = 0;
getEmptyPos(status, x, y);
int nx = x + dx[i];
int ny = y + dy[i];
if (nx < 0 || nx > 2 || ny < 0 || ny > 2) {
return -1; }
int lasP = 3 * x + y;
int newP = 3 * nx + ny;
long long newStatus = status;
newStatus ^= getStatus(status, nx, ny) << (4 * newP); // 将nx, ny变为空格
newStatus |= p[getStatus(status, nx, ny)][i] << (4 * lasP); // 将x, y填入新的状态
return newStatus;
}
int bfs(queue<State> &q, int nowQ)
{
int size = q.size();
while (size--) {
State now = q.front();
q.pop();
if (now.first >= 30) {
continue; }
for (int i = 0; i < 4; i++) {
State newState;
newState.first = now.first + 1;
newState.second = transfer(now.second, i);
if (newState.second == -1) {
continue; }
if (inQ.count(newState.second)) {
if (inQ[newState.second] == 1 - nowQ) {
ans = dis[newState.second] + now.first + 1;
if (ans > 30) {
ans = -1; }
return 0;
} else {
continue;
}
}
dis[newState.second] = newState.first;
inQ[newState.second] = nowQ;
q.push(newState);
}
}
return -1;
}
void dBfs()
{
if (inQ.count(initStatus)) {
ans = 0;
return;
}
ans = -1;
inQ[initStatus] = 0;
dis[initStatus] = 0;
q[0].push({
0, initStatus});
while (!q[0].empty() && !q[1].empty()) {
if (q[0].size() <= q[1].size()) {
if (bfs(q[0], 0) == 0) {
return; }
} else {
if (bfs(q[1], 1) == 0) {
return; }
}
}
}
int main()
{
while (cin >> x >> y && (x != 0 || y != 0)) {
x--;
y--;
swap(x, y); // 习惯横着编号
for (int i = 0; i < 9; i++) {
cin >> s[i]; }
init();
dBfs();
cout << ans << endl;
}
return 0;
}