一直追尋直到哭泣之時
阿新 • • 發佈:2018-12-16
題
如下的 3x3 矩陣裡,x 可以與相鄰的數字交換位置。
1 2 3
x 4 6
7 5 8
遊戲目的是達到下面的局面:
1 2 3
4 5 6
7 8 x
輸入一個遊戲局面,輸出可以達成目的的操作序列(每一步 x 與哪個方向的數交換)。如果不能達到目的,輸出 unsolvable
。
任何一種可行的解都可以達到目的。
解
從目標局面反向搜尋。共計有 181440 個可以解的答案,將答案都儲存起來待之後查詢。樸素地將所有的狀態用 std::string
儲存得到了 MLE。但我不願寫康託展開,改而將位置數字壓縮到長整數中,x 以 0 表示。
到此可通過這個題。但我的資源消耗還是比較多的,可能資料量比較少每組直接搜尋可以通過吧。可以考慮康託展開壓縮空間;發現最大答案只有 31 個步驟,所以答案可以壓縮到 2x31 = 62 位的整數裡,再壓縮空間;還可以將詢問都儲存起來,搜尋的時候搜到所有的答案即停止,可以壓縮時間和空間。
碼
#include <iostream>
#include <queue>
#include <map>
#include <unordered_map>
#include <string>
#include <functional>
#include <algorithm>
#include <cstdint>
#include <cassert>
typedef unsigned long long ull;
typedef unsigned uint;
bool GetNextCase (std::string& s) {
s.clear();
static char tmp[3];
for (int i = 0; i < 9; ++i) {
std::cin >> tmp;
if (!std::cin) {
return false;
}
s += tmp[0];
}
return true;
}
ull StagePack(std::string s) {
std::reverse(s.begin(), s.end());
ull p = 0;
for (int i = 0; i < 9; ++ i) {
p <<= 4;
if (s[i] == 'x') {
p |= 0;
} else {
p |= s[i] - '0';
}
}
return p;
}
uint GetStagePack(ull p, int i) {
return (uint) ((p >> (4 * i)) & 0xf);
}
ull SetStagePack(ull p, int i, ull v) {
i *= 4;
p = ((p & (~(ull{0xf} << i))) | (v << i));
return p;
}
std::string UnpackStage(ull p) {
std::string s;
for (int i = 0; i < 9; ++i) {
int si = (int)(p & 0xf);
assert(si >= 0 && si < 9);
if (si == 0) {
s += 'x';
} else {
s += (char)('0' + si);
}
p >>= 4;
}
return s;
}
ull StagePackSwap(ull p, int i, int j) {
uint pi = GetStagePack(p, i);
uint pj = GetStagePack(p, j);
p = SetStagePack(p, i, pj);
p = SetStagePack(p, j, pi);
return p;
}
struct Stage {
ull stage;
int xpos;
};
typedef std::map<ull, std::string> SolutionMap;
SolutionMap GetSolutions() {
Stage initial;
initial.stage = StagePack("12345678x");
initial.xpos = 8;
std::queue<Stage> blob;
blob.emplace(initial);
SolutionMap sln;
sln[initial.stage] = "";
std::array<int, 9> step[4];
{
auto& lstep = step[0];
for (int i = 0; i < 9; ++i) {
lstep[i] = i - 1;
if (i % 3 == 0) {
lstep[i] = -1;
}
}
auto& rstep = step[1];
for (int i = 0; i < 9; ++i) {
rstep[i] = i + 1;
if (i % 3 == 2) {
rstep[i] = -1;
}
}
auto& ustep = step[2];
for (int i = 0; i < 9; ++i) {
ustep[i] = i - 3;
}
auto& dstep = step[3];
for (int i = 0; i < 9; ++i) {
dstep[i] = i + 3;
}
}
char step_name[4] = {'r', 'l', 'd', 'u'};
while (!blob.empty()) {
Stage curr = blob.front();
blob.pop();
int xpos = curr.xpos;
for (int i = 0; i < 4; ++i) {
int nxpos = step[i][xpos];
if (nxpos < 0 || nxpos >= 9) {
continue;
}
ull stage = curr.stage;
stage = StagePackSwap(stage, xpos, nxpos);
auto iter_next = sln.find(stage);
if (iter_next != sln.end()) {
continue;
}
sln.emplace(stage, sln[curr.stage] + step_name[i]);
Stage next{stage, nxpos};
blob.emplace(std::move(next));
}
if (sln.size() > 200000) {
std::cerr << "large solution size\n";
break;
}
}
return sln;
}
int main() {
auto sln = GetSolutions();
std::string s;
while (GetNextCase(s)) {
auto iter = sln.find(StagePack(s));
if (iter == sln.end()) {
std::cout << "unsolvable\n";
} else {
std::string res = iter->second;
std::reverse(res.begin(), res.end());
std::cout << res << std::endl;
}
}
return 0;
}