题目 1426: [蓝桥杯][历届试题]九宫重排
题目描述:
如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。
我们把第一个图的局面记为:12345678.
把第二个图的局面记为:123.46758
显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。
样例输入:
12345678.
123.46758
样例输出:
3
解题思路:
1、对于求最短路径选择bfs;
2、然后要进行排重可以使用哈希函数map映射或者使用康拓判重;
康拓公式:X=an*(n-1)!+an-1*(n-2)!+…+ai*(i-1)!+…+a21!+a10!,
an代表的是当前这个数的后面比他小的有几位。
样例:数231在123的全排列下面是第X+1个数
2的后面有1个数比2小,即 1 * 2!
3的后面有1比3小的数,即 1* 1!
1的后面没有比1小的数,即 0 * 0!
X = 12!+11!+0*0! = 3
123的全排列: 123 132 213 231 312 321
代码一:使用map判重(耗时:948)
借鉴了:
https://blog.csdn.net/qq_41923622/article/details/86649189
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<map>
#include<cstring>
using namespace std;
char start[3][3];//记录开始的状态
char gogal[3][3];// 记录目标状态
struct node {
int x;//记录空白行的位置
int y;//记录空白列的位置
int step;//走的步数记录
char map[3][3];//记录状态
};
queue<node>q;//这个其实是bfs的精华部分,就是利用他才可以算出最短路径
map<string,int>v;
int dx[]={-1,0,1,0};//上左下右
int dy[]={0,-1,0,1};
int judge(node cur){//判断是否达到要求
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
if(cur.map[i][j]!=gogal[i][j]){
return 0;
}
}
}
return 1;
}
int check(node next){//这里就算是判重
string s="";
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++){
s+=next.map[i][j];//把数字组成字符串
}
}
if(v[s]>0) return 0; //因为map定义string,int ,我们学过map的话知道int的值是靠string来寻找,如果string这个字符串的int 大于0的话那么之前就一定有
v[s]++;//这个就是累加字符串出现的次数
return 1;
}
int bfs(int x1,int y1){
node cur,next;//定义2个结构体变量
cur.x=x1;
cur.y=y1;
cur.step=0;
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
cur.map[i][j]=start[i][j];
}
}//上面是把当前状态放进结构体中
if(judge(cur)){//判断第一次是不是就是目标,这个是特殊情况可能存在
return cur.step;
}
q.push(cur);//把当前状态放进去,其实这也是bfs中的模板
while(!q.empty()) {
cur=q.front();//取出队列中的第一个
q.pop();//取出之后就要删除,标准用语就是出队
for(int i=0;i<4;i++){
next.x=cur.x+dx[i];
next.y=cur.y+dy[i];
if(next.x>=0&&next.x<3&&next.y>=0&&next.y<3){//这里主要是判断下标是否越界,如果越界就相当于当前的方向是不可以走
next.step=cur.step+1;
for(int j=0;j<3;j++)
{
for(int k=0;k<3;k++){
next.map[j][k]=cur.map[j][k];
}
}
swap(next.map[next.x][next.y],next.map[cur.x ][cur.y]);//得到可以移动的位置,这里是实现移动
if(check(next)){//移动了之后,现在就要进行1、把他写入map中进行判重,2.进行判断是个是终极目标
if(judge(next)){
return next.step;
}
q.push(next);
}
}
}
}
return -1;//如果不能实现这个方案就是放回-1
}
int main(){
int x1,y1;//记录起始位置
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++){
cin>>start[i][j];
if(start[i][j]=='.'){
x1=i;
y1=j;
}
}
}
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
cin>>gogal[i][j];
}
}
cout<<bfs(x1,y1)<<endl;
return 0;
}
代码二:使用康拓公式 (耗时:249)
借鉴:
https://blog.csdn.net/Crystal_viv/article/details/79386871
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
char start[3][3];//记录开始的状态
char gogal[3][3];// 记录目标状态
struct node {
int x;//记录空白行的位置
int y;//记录空白列的位置
int step;//走的步数记录
char map[3][3];//记录状态
};
queue<node>q;//这个其实是bfs的精华部分,就是利用他才可以算出最短路径
int kantuo[362880]; //9!=362880,进行判断是否出现重复
int a[9];
int fac[]={1,1,2,6,24,120,720,5040,40320};//这个是数字1-8的阶乘值
int dx[]={-1,0,1,0};//上左下右
int dy[]={0,-1,0,1};
int judge(node cur){//判断是否达到要求
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
if(cur.map[i][j]!=gogal[i][j]){
return 0;
}
}
}
return 1;
}
int check(node next){//这里就算是判重
int x=0;
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
a[x++]=next.map[i][j]-'0';
}
}
int sum=0,s=0;//s代表第几个数
for(int i=0;i<9;i++){
for(int j=i+1;j<9;j++){
if(a[i]>a[j]){
sum++;
}
}
s+=sum*fac[8-i];
sum=0;
}
if(kantuo[s]>0) {
return 0;
}
kantuo[s]++;
return 1;
}
int bfs(int x1,int y1){
node cur,next;//定义2个结构体变量
cur.x=x1;
cur.y=y1;
cur.step=0;
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
cur.map[i][j]=start[i][j];
}
}//上面是把当前状态放进结构体中
if(judge(cur)){//判断第一次是不是就是目标,这个是特殊情况可能存在
return cur.step;
}
q.push(cur);//把当前状态放进去,其实这也是bfs中的模板
while(!q.empty()) {
cur=q.front();//取出队列中的第一个
q.pop();//取出之后就要删除,标准用语就是出队
for(int i=0;i<4;i++){
next.x=cur.x+dx[i];
next.y=cur.y+dy[i];
if(next.x>=0&&next.x<3&&next.y>=0&&next.y<3){//这里主要是判断下标是否越界,如果越界就相当于当前的方向是不可以走
next.step=cur.step+1;
for(int j=0;j<3;j++)
{
for(int k=0;k<3;k++){
next.map[j][k]=cur.map[j][k];
}
}
swap(next.map[next.x][next.y],next.map[cur.x ][cur.y]);//得到可以移动的位置,这里是实现移动
if(check(next)){//移动了之后,现在就要进行1、把他写入map中进行判重,2.进行判断是个是终极目标
if(judge(next)){
return next.step;
}
q.push(next);
}
}
}
}
return -1;//如果不能实现这个方案就是放回-1
}
int main(){
int x1,y1;//记录起始位置
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++){
cin>>start[i][j];
if(start[i][j]=='.'){
x1=i;
y1=j;
}
}
}
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
cin>>gogal[i][j];
}
}
cout<<bfs(x1,y1)<<endl;
return 0;
}
原题链接:https://www.dotcpp.com/oj/problem1426.html