魔板
状态串 BFS 按 ABC 顺序扩展取字典序
正文
// 原题:https://oj.yecheng.tv/p/T1449
// 题意:魔板 8 个格子按顺时针编号,三种操作 A(上下两行交换)、B(最右列插到最左)、C(中间四格顺时针转),求从基本状态到目标状态的最短操作序列(并列时取字典序最早)。
// 思路:BFS 打表。
// 1. 状态用 8 个字符的串表示,从基本状态 "12345678" 出发。
// 2. 三种操作按 A、B、C 的顺序扩展,BFS 首次到达某状态时得到的就是最短且字典序最早的路径。
// 3. 状态数只有 8! = 40320,直接广搜打表即可。
// 复杂度:O(8!) 时间 / O(8!) 空间
// 易错点:状态的排列顺序是「上行从左到右,再下行从右到左」,搞反了操作函数就全错。
// 易错点:要按 A、B、C 的顺序扩展才能得到字典序最早的解,改成 C、B、A 会得到另一个等长解。
#include <bits/stdc++.h>
using namespace std;
string moveA(string s){
reverse(s.begin(), s.end());
return s;
}
string moveB(string s){
return string({s[3], s[0], s[1], s[2], s[5], s[6], s[7], s[4]});
}
string moveC(string s){
return string({s[0], s[6], s[1], s[3], s[4], s[2], s[5], s[7]});
}
int main(){
string target;
for(int i = 0; i < 8; i++){
int x;
if(!(cin >> x)) return 0;
target += char('0' + x);
}
map<string, string> dis;
queue<string> q;
dis["12345678"] = "";
q.push("12345678");
while(!q.empty()){
string s = q.front();
q.pop();
string nxt[3] = {moveA(s), moveB(s), moveC(s)};
char name[3] = {'A', 'B', 'C'};
for(int k = 0; k < 3; k++){
if(dis.count(nxt[k])) continue;
dis[nxt[k]] = dis[s] + name[k];
q.push(nxt[k]);
}
}
string ans = dis[target];
cout << ans.size();
if(!ans.empty()) cout << " " << ans;
cout << "\n";
return 0;
}
一本通·提高篇的其它内容
- 活动安排
- 种树
- 喷水装置
- 加工生产调度
- 智力大冲浪
- 数列极差
- 数列分段
- 线段
- 家庭作业
- 钓鱼
- 糖果传递
- 愤怒的牛
- Best Cow Fences
- 曲线
- 数列分段 II
- 扩散
- 灯泡
- 传送带
- 数的划分
- 生日蛋糕
- 小木棍
- Addition Chains
- 埃及分数
- 平板涂色
- 质数方阵
- 靶形数独
- 电路维修
- Knight Moves
- 棋盘游戏
- Keyboarding
- 移动玩具
- 山峰和山谷
- Oulipo
- 图书管理
- Power Strings
- Seekthe Name, Seek the Fame
- Friends
- A Horrible Poem
- Beads
- Antisymmetry
- 门票
- 收集雪花
- 剪花布条
- Power Strings
- Radio Transmission
- OKR-Periods of Words
- 似乎在梦中见过的样子
- Censoring
- Phone List
- The XOR Largest Pair
- Nikitosh 和异或
- Immediate Decodability
- L 语言
- Secret Message 秘密信息
- 背单词
- The Xor-longest Path
- Keywords Search
- 玄武密码
- Censoring
- 单词