题解_八数码

文章介绍了一种在3x3棋盘上,从给定的初始布局移动到目标布局的最少步骤算法。通过康托展开和逆运算节省存储空间,并使用宽度优先搜索策略寻找最少移动次数。代码示例展示了如何处理这个问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

真·题·再·现

【题目描述】

3×3的棋盘上,摆有八个棋子,每个棋子上标有18的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局,为了使题目简单, 设目标状态为

1 2 3 
8 0 4 
7 6 5 

找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。

【输入描述】

三行,每行三个整数,表示方阵的开始状态

【输出描述】

只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数,若在5000步内无解,则输出-1

【输入样例】

1 2 3
8 4 0
7 6 5

【输出样例】

1

解·题·思·路

【题目分析】

这道题目有十分甚至九分的复杂,需要同时涉及矩阵、宽度优先搜索、康托展开和康托展开逆运算四个知识点,所以本期题解可能会比较长。
首先,为了方便声明visited数组,我们需要将矩阵转为整型变量。
这时有一个问题:矩阵转化的整型变量最大值事876543210,也就是说布尔型visited数组会占用876543210字节的空间,而题目要求的字节只有134217728字节,所以存储空间不够用力!(大悲)
但在仔细看看:从786543210801234567之间有14691356个数,这些数都没有对应的情况,白白浪费了14691356个字节,所以说刚才说的这种方法浪费了很多空间。这时,就需要用到康托展开节省这些空间。
空间省出来之后,还需要用康托展开逆运算转换回矩阵形态。

【解题过程】

由于本期题目过于复杂,直接上代码,大家可以自己分析一下。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=370000;
int fac[15]={1,1,2,6,24,120,720,5040,40320,362880};//记录1-10的阶乘
bool vis[MAXN];
int step[MAXN];
int q1[MAXN],q2[MAXN],front,rear;//q1放数字值,q2放康托压缩后的值
int Cantor(int a[],int n){
	int s=1;
	int sum=1;
	for(int i=0;i<n;i++){
		s=0;
		for(int j=i+1;j<n;j++)if(a[j]<a[i])s++;
		sum+=s*fac[n-1-i];
	}
	return sum;
}
int getnum(int a[]){
	int ans=0;
	for(int i=0;i<9;i++) {
		ans*=10;
		ans+=a[i];
	}
	return ans;
}
int main(){
	memset(step,-1,sizeof(step));
	int a[9]={1,2,3,8,0,4,7,6,5};
	int end=getnum(a);
	int endCantor=Cantor(a,9);
	for(int i=0;i<9;i++)cin>>a[i];
	int begin=getnum(a);
	int beginCantor=Cantor(a,9);
	vis[beginCantor]=1;
	step[beginCantor]=0;
	q1[rear]=begin;
	q2[rear]=beginCantor;
	rear++;
	if(begin==end){
		cout<<0<<endl;
		return 0;
	}
	while(front<rear){
		int temp=q1[front];
		int tempCantor=q2[front];
		front++;
		int find;
		int t=temp;
		for(int i=8; i>=0; i--){
			a[i]=t%10;
			t/=10;
			if(a[i]==0)find=i;
		}
		if(find>=3){
			swap(a[find],a[find-3]);
			t=Cantor(a,9);
			if(!vis[t]){
				vis[t]=1;
				step[t]=step[tempCantor]+1;
				q2[rear]=t;
				t=getnum(a);
				if(t==end)break;
				q1[rear]=t;
				rear++;
			}
			swap(a[find],a[find-3]);
		}
		if((find+1)%3!=0){
			swap(a[find],a[find+1]);
			t=Cantor(a,9);
			if(!vis[t]){
				vis[t]=1;
				step[t]=step[tempCantor]+1;
				q2[rear]=t;
				t=getnum(a);
				if(t==end)break;
				q1[rear]=t;
				rear++;
			}
			swap(a[find],a[find+1]);
		}
		if(find<=5){
			swap(a[find],a[find+3]);
			t=Cantor(a,9);
			if(!vis[t]){
				vis[t]=1;
				step[t]=step[tempCantor]+1;
				q2[rear]=t;
				t=getnum(a);
				if(t==end)break;
				q1[rear]=t;
				rear++;
			}
			swap(a[find],a[find+3]);
		}
		if(find%3!=0){
			swap(a[find],a[find-1]);
			t=Cantor(a,9);
			if(!vis[t]){
				vis[t]=1;
				step[t]=step[tempCantor]+1;
				q2[rear]=t;
				t=getnum(a);
				if(t==end)break;
				q1[rear]=t;
				rear++;
			}
			swap(a[find],a[find-1]);
		}
	}
	if(step[endCantor]==-1)cout<<-1;
	else cout<<step[endCantor];
	return 0;
}

【测试结果】

全部样例均正确,压力马斯内!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值