洛谷 P10578 [蓝桥杯 2024 国 A] 旋转九宫格
此题解对arcmiao的解释,转载证明

题目内容
P10578 [蓝桥杯 2024 国 A] 旋转九宫格
题目描述
给定一个 3 × 3 3\times 3 3×3 的九宫格,每个格子内分别含有一个数字,每个格子里的数字互不相同。每步我们可以选择任意一个 2 × 2 2\times 2 2×2 的区域将其顺时针旋转,例如:
例如1 2 3 4 5 6 7 8 9将其旋转右上角,可得:
1 5 2 4 6 3 7 8 9问最少需要几步才能将给定的状态旋转为
1 2 3 4 5 6 7 8 9输入格式
输入的第一行包含一个整数 T T T 表示询问的组数。
接下来依次输入每组询问。
每组询问包含三行,每行包含三个数,表示询问的九宫格的状态。输出格式
输出 T T T 行,每行包含一个整数表示本次询问的答案。
输入输出样例 #1
输入 #1
2 1 2 3 4 5 6 7 8 9 1 5 2 4 6 3 7 8 9输出 #1
0 3说明/提示
对于 60 % 60\% 60% 的评测用例, T = 1 T=1 T=1;
对于所有评测用例, T ≤ 1 0 5 T\le 10^5 T≤105。
题意
题意很简洁,在一个 3 × 3 3\times3 3×3 的九宫格 中,可以任意选择 2 × 2 2\times2 2×2 的区域顺时针旋转,问你最少多少次旋转后九宫格成为:
1 2 3
4 5 6
7 8 9
算法建议
这题我建议使用BFS,我来举个例子:假如你的眼镜掉到地上了,那你肯定会向四周去摸索(BFS),如果你有一个球,掉到了地上,而且你知道方向,这时肯定会向着一个方向一直找(DFS)。而且,就算使用DFS,也会超时我被挂了一下午
算法思路
首先,用一个9个字符字符串量记录一下解,如果使用二维来记录的话大概率要超时,所以不如压缩成一维的字符串。
但是这样还是可能超时,还需要使用 map 进行离散(也可能是我不会优化)。
接着就是DFS函数部分,先开一个队列,把标准的答案 123456789 入队列,然后重复模拟旋转,判断当前状态是否被访问过,,若新状态没有被访问,就把当前旋转次数加一如果达到目标,就停止,否则继续入队列搜索。采用逆推的办法,速度更快。
代码解释
BFS判断、模拟旋转部分:
逆推解决,通过标准的九宫格去推断旋转的最小的次数,然后优化,如果达到标准就没必要继续查询,可以直接返回。
void bfs(){
q.push(str);
h[str]=1;
while(q.size()){
string s=q.front();
q.pop();
string v[4]={s,s,s,s};//这里的 s 代表的不是字符 s,而是 s 字符串。
v[0][0]=s[1];v[0][4]=s[3];
v[0][1]=s[4];v[0][3]=s[0];
//左上角区域逆时针旋转
//v[0][0]=s[1]; 位置0 ←位置1的值
//v[0][4]=s[3]; 位置1 ←位置4的值
//v[0][1]=s[4]; 位置4 ←位置0的值
//v[0][3]=s[0]; 位置4 ←位置3的值
v[1][1]=s[2];v[1][5]=s[4];//右上角区域逆时针旋转
v[1][2]=s[5];v[1][4]=s[1];
v[2][3]=s[4];v[2][6]=s[3];//左下角区域逆时针旋转
v[2][4]=s[7];v[2][7]=s[6];
v[3][4]=s[5];v[3][7]=s[4];//右下角区域逆时针旋转
v[3][5]=s[8];v[3][8]=s[7];
//因为这里是逆推,所以要逆时针旋转。
for(int i=0;i<4;i++) {
if (!h[v[i]]){//判断新状态是否被访问过
h[v[i]]=h[s]+1;
if (v[i]==str) break;//如果回到标注就可以停了,也算是优化。
q.push(v[i]);
}
}
}
}
主函数部分
字符c读入的时候是可以读进去换行符号的,所以不需要进行特判
int main(){
cin>>t;
bfs();
while(t--){
string s;
for (int i=0;i<9;i++) {
char c;
cin>>c;
s+=c;//把读取到的字符拼接到一起,字符 c 会读取换行。
}
cout<<h[s]-1<<endl;//减一是因为初始状态记为一
}
return 0;
}
代码
和arcmiao的代码几乎一样,主要看注释。
#include <bits/stdc++.h>
using namespace std;
string str="123456789";//题目要求的答案,也就是标准 。
map<string,int>h;//记录每个状态达到标准需要的最小步数。
queue<string>q;
int t;
void bfs(){
q.push(str);
h[str]=1;
while(q.size()){
string s=q.front();
q.pop();
string v[4]={s,s,s,s};//这里的 s 代表的不是字符 s,而是 s 字符串。
v[0][0]=s[1];v[0][4]=s[3];
v[0][1]=s[4];v[0][3]=s[0];
//左上角区域逆时针旋转
//v[0][0]=s[1]; 位置0 ←位置1的值
//v[0][4]=s[3]; 位置1 ←位置4的值
//v[0][1]=s[4]; 位置4 ←位置0的值
//v[0][3]=s[0]; 位置4 ←位置3的值
v[1][1]=s[2];v[1][5]=s[4];//右上角区域逆时针旋转
v[1][2]=s[5];v[1][4]=s[1];
v[2][3]=s[4];v[2][6]=s[3];//左下角区域逆时针旋转
v[2][4]=s[7];v[2][7]=s[6];
v[3][4]=s[5];v[3][7]=s[4];//右下角区域逆时针旋转
v[3][5]=s[8];v[3][8]=s[7];
//因为这里是逆推,所以要逆时针旋转。
for(int i=0;i<4;i++) {
if (!h[v[i]]){//判断新状态是否被访问过
h[v[i]]=h[s]+1;
if (v[i]==str) break;//如果回到标注就可以停了,也算是优化。
q.push(v[i]);
}
}
}
}
int main(){
cin>>t;
bfs();
while(t--){
string s;
for (int i=0;i<9;i++) {
char c;
cin>>c;
s+=c;//把读取到的字符拼接到一起,字符 c 会读取换行。
}
cout<<h[s]-1<<endl;//减一是因为初始状态记为一
}
return 0;
}
如果对你有帮助,点个赞再走吧,谢谢!
1038

被折叠的 条评论
为什么被折叠?



