题目描述
#10027. 「一本通 1.4 例 2」魔板 - 题目 - LibreOJ (loj.ac) 原题来自:USACO 3.2.5
Rubik 先生在发明了风靡全球魔方之后,又发明了它的二维版本——魔板。这是一张有 个大小相同的格子的魔板:
1 2 3 4
8 7 6 5
我们知道魔板的每一个方格都有一种颜色。这 种颜色用前 个正整数来表示。可以用颜色的序列来表示一种魔板状态,规定从魔板的左上角开始,沿顺时针方向依次取出整数,构成一个颜色序列。对于上图的魔板状态,我们用序列 来表示。这是基本状态。
这里提供三种基本操作,分别用大写字母 A
,B
,C
来表示(可以通过这些操作改变魔板的状态):
A
:交换上下两行;B
:将最右边的一列插入最左边;C
:魔板中央作顺时针旋转。
下面是对基本状态进行操作的示范:
A
:
8 7 6 5
1 2 3 4
B
:
4 1 2 3
5 8 7 6
C
:
1 7 2 4
8 6 3 5
对于每种可能的状态,这三种基本操作都可以使用。
你要编程计算用最少的基本操作完成基本状态到特殊状态的转换,输出基本操作序列。
输入格式
输入仅一行,包括 个整数,用空格分开,表示目标状态。
输出格式
输出文件的第一行包括一个整数,表示最短操作序列的长度。 第二行为在字典序中最早出现的操作序列。
样例
输入
2 6 8 4 5 7 3 1
输出
7
BCABCCB
数据范围与提示
输入数据中的所有数字均为 到 之间的整数。
思路
把当前魔板状态看做一个节点, 可以进行一系列操作来转换到另一个节点, 显然需要解决四个问题:
- 如何建立数据结构来表示当前节点的魔板状态 结构体可以解决
- 如何判断当前魔板状态是最终要求的状态 挨个判断即可
- 如何从一个状态转移到另一个状态 有状态就很好办
- 如何在转移状态时记录当前路径 类似于BFS求方案, 从终末状态逆向泛洪, 记录prev数组指向上一个和当前走的操作。
存状态通常使用哈希法, 把整个状态用一个最小整数存下来。 可以自己手写哈希, 或者用康拓展开, 或者用STL的map/unordered_map
代码
char g[2][4];
unordered_map<string, int> dist;
unordered_map<string, pair<int, string>> pre;
queue<string> q;
string start, endstate;
void set(string s)
{
for (int i = 0; i < 4; i++)
g[0][i] = s[i];
for (int i = 3, j = 4; i >= 0; i--, j++)
g[1][i] = s[j];
}
string get()
{
string s;
for (int i = 0; i < 4; i++)
s += g[0][i];
for (int i = 3; i >= 0; i--)
s += g[1][i];
return s;
}
string move0(string t)
{
set(t);
for (int i = 0; i < 4; i++)
swap(g[0][i], g[1][i]);
return get();
}
string move1(string t)
{
set(t);
char v0 = g[0][3], v1 = g[1][3];
for (int i = 3; i >= 0; i--)
g[0][i] = g[0][i - 1], g[1][i] = g[1][i - 1];
g[0][0] = v0, g[1][0] = v1;
return get();
}
string move2(string t)
{
set(t);
char v0 = g[0][1];
g[0][1] = g[1][1];
g[1][1] = g[1][2];
g[1][2] = g[0][2];
g[0][2] = v0;
return get();
}
void bfs(string start, string end)
{
q.push(end);
dist[end] = 0;
while (!q.empty())
{
auto t = q.front();
q.pop();
string m[3];
m[0] = move0(t);
m[1] = move1(t);
m[2] = move2(t);
for (int i = 0; i < 3; i++)
{
string str = m[i];
if (dist.count(str))
continue;
dist[str] = dist[t] + 1;
pre[str] = {char(i + 'A'), t};
if (str == start)
return;
q.push(str);
}
}
}
int main()
{
for (int i = 0; i < 8; i++)
{
int t;
cin >> t;
start += char(t + '0');
endstate += char(i + '1');
}
bfs(start, endstate);
cout << dist[start] << endl;
string res;
while (start != endstate)
{
res += pre[start].first;
start = pre[start].second;
}
rev(res);
if (res.size())
cout << res << endl;
return 0;
}