问题描述
如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。
我们把第一个图的局面记为:12345678.
把第二个图的局面记为:123.46758
显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。
输入格式
输入第一行包含九宫的初态,第二行包含九宫的终态。
输出格式
输出最少的步数,如果不存在方案,则输出-1。
样例输入
12345678.
123.46758
样例输出
3
样例输入
13524678.
46758123.
样例输出
22
分析题目:一开始我以为是要建一个二维图,但是建了一个图之后发现不太能够标记走过的路径,其实不需要的,这道题目可以这样去想,用一个一维数组去存放,然后每一种情况就用对应的十进制数来表示,存放在set里面用于判断是否存在这样的情况就好了,这样对应的二位数组的下标就用一位数组的下标来表示了,举个栗子:一位数组为6时,对应是二维数组的下标就是map[6/3][6%3]了,然后依据这个方式去用队列广搜就可以了
#include<iostream>
#include<cstdio>
#include<cmath>
#include<set>
#include<queue>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
const int N=1e5+10;
typedef long long ll;
#define rep(i,a,b) for(int i=a;i<=b;i++)
const int INF=0x3f3f3f3f;
int n,m,ans;
char str[20];
//typedef int E[10];
struct node{
int step;
int e[9];
};
queue<node>q;
set<int>se;
node disn;
int dx[]={1,-1,0,0};
int dy[]={0,0,1,-1};
bool check(node p){
int sum=0;
rep(i,0,8) sum=sum*10+p.e[i];
if(se.find(sum)!=se.end())return false;
se.insert(sum);
return true;
}
void dfs(node p0){
p0.step=0;
q.push(p0);
while(q.size()){
node p=q.front();
q.pop();
if(memcmp(p.e,disn.e,sizeof disn.e)==0){
ans=p.step;
return ;
}
int k=0;
for(k=0;k<9;k++)
if(p.e[k]==0) break;
int x=k/3;
int y=k%3;
for(int i=0;i<4;i++){
int xx=x+dx[i];
int yy=y+dy[i];
if(xx<0||yy<0||xx>2||yy>2) continue;
int pp=xx*3+yy;
node res;
memcpy(res.e,p.e,sizeof p.e);
swap(res.e[pp],res.e[k]);
if(check(res)){
res.step=p.step+1;
q.push(res);
}
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif // ONLINE_JUDGE
scanf("%s",str);
node p;
rep(i,0,8){
if(str[i]=='.') p.e[i]=0;
else p.e[i]=str[i]-'0';
}
scanf("%s",str);
rep(i,0,8){
if(str[i]=='.') disn.e[i]=0;
else disn.e[i]=str[i]-'0';
}
ans=-1;
dfs(p);
printf("%d\n",ans);
return 0;
}
然后这一道题目还可以用[康托展开]来去处理然后用哈希去判断,其实这样去做和上面的方法差不多(https://blog.csdn.net/wbin233/article/details/72998375)
#include<iostream>
#include<string.h>
#include<cstdio>
#define M 1000000
using namespace std;
typedef int type[9];
type qs,mb,gc[M];
int sept[M]; //保存步数
int dir[4][2] = {-1,0,0,-1,0,1,1,0},st=0;
int step[M]={0};
int fact[10]={1},vis[M];
void init() //初始化 阶乘
{
int i;
for (i=1; i<=9; i++)
{
fact[i] = i*fact[i-1];
}
}
int kangtuo(int *a)
{
int num=0,t,i,j;
for (i=0; i<8; i++)
{
t = 0;
for (j=i+1; j<9; j++)
{
if (a[i] > a[j])
t++;
}
num += t * fact[8-i];//num为展开的第几个排列
}
if (vis[num] == 1)
return 0;
vis[num] = 1;
return 1;
}
int bfs()
{
int front=0,rear=1,i,j,k,c,x,y,xx,yy;
memcpy(gc[st],qs,sizeof(qs)); //把起始状态放入
while (front < rear)
{
type temp;
memcpy(temp,gc[front],sizeof(gc[front])); //获取对头
if (memcmp(temp,mb,sizeof(mb))==0)//到达终点
{
return front;
}
for(k=0; k<9; k++)
{
if (temp[k]==0)
break;
}
x = k/3;
y = k%3; //转2维下标
for(i=0; i<4; i++)
{
xx = dir[i][0] + x;
yy = dir[i][1] + y;
if (xx>=0&&yy>=0&&xx<3&&yy<3)
{
c = 3*xx + yy;
type temp2;
memcpy(temp2,temp,sizeof(temp));
temp2[k] = temp2[c];
temp2[c] = 0; //进行交换
if (kangtuo(temp2))//康托展开 ,判重
{
memcpy(gc[rear],temp2,sizeof(temp2));
step[rear++] = step[front] + 1;
}
}
}
front++;//进入下一个队列元素
}
return -1;
}
int main()
{
char ch;
int i,r;
init();
for (i=0; i<9; i++)
{
cin>>ch;
ch == '.' ? qs[i]=0 : qs[i]=ch-'0';
}
getchar();
for (i=0 ;i<9; i++)
{
cin>>ch;
ch == '.' ? mb[i]=0 : mb[i]=ch-'0';
}
r = bfs();
r == -1 ? cout<<r<<endl : cout<<step[r]<<endl;
return 0;
}