算法笔记练习 8.2 广度优先搜索(BFS) 问题 C: 【宽搜入门】8数码难题

算法笔记练习 题解合集

本题链接

题目

题目描述
初始状态的步数就算1,哈哈

输入:第一个33的矩阵是原始状态,第二个33的矩阵是目标状态。
输出:移动所用最少的步数

Input

2 8 3
1 6 4
7 0 5
1 2 3
8 0 4
7 6 5

Output

6

思路

0. 如何表示当前状态?

因为 3×3 的矩阵很小,就不用二维数组了,用一个一维数组即可:

		1 2 3
		4 5 6
		7 8 9

如上所示,用下标 1 ~ 9 来表示 9 个位置上的数。
例如对于以下这样的状态,用一维数组来表示就是{1, 0, 2, 3, 4, 5, 6, 7, 8}

		1 0 2
		3 4 5
		6 7 8

1. 下一个合法的状态是什么?

解决了表示的问题,对于 BFS 来说最重要的就是合法状态的推导。例如对于下方左侧假设的情况,0 表示空着的位置,那么 0 附近的 1、2、4 都可以移动到 0 的位置。

从一维数组的变化来看,其实就是 0 附近的数字都可以与其交换,例如和 4 交换就得到右边的情况。

		1 0 2				1 4 2
		3 4 5		->		3 0 5
		6 7 8				6 7 8

因此可以根据 0 交换(移动)的方位来分类讨论:

  • 0 和其左侧数字交换:当且仅当 0 的下标zero满足zero % 3 != 1时不会越界,同时与其交换的下标是zero - 1
  • 0 和其上侧数字交换:当且仅当 0 的下标zero满足zero > 3是不会越界,同时与其交换的下标是zero - 3;
  • ……

右侧和下侧的情况非常相似,具体实现见代码。

2. 如何保存目前的移动次数?

map<vector<int>, int> moveCnt;

个人喜欢用映射实现,因为映射既可以做到继承队首节点的移动次数,又可以顺便实现查重功能(已经出现过的状态不能再次出现,否则会无限循环)。具体见代码。

代码

#include <iostream>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
using namespace std;

vector<int> beg(10), fin(10);
map<vector<int>, int> moveCnt;

void BFS() {
	moveCnt[beg] = 1;
	queue<vector<int> > Q;
	Q.push(beg);
	while (!Q.empty()) {
		vector<int> top = Q.front();
		int nowCnt = moveCnt[top], zero; 
		if (top == fin) {
			printf("%d\n", nowCnt);
			return;
		} 
		Q.pop();
		for (zero = 1; zero <= 9; ++zero)
			if (top[zero] == 0)
				break;
		if (zero > 3) {
			swap(top[zero], top[zero - 3]);
			if (!moveCnt.count(top)) {
				moveCnt[top] = nowCnt + 1;
				Q.push(top);
			} 
			swap(top[zero], top[zero - 3]);
		}
		if (zero < 7) {
			swap(top[zero], top[zero + 3]);
			if (!moveCnt.count(top)) {
				moveCnt[top] = nowCnt + 1;
				Q.push(top);
			} 
			swap(top[zero], top[zero + 3]);
		}
		if (zero % 3 != 1) {
			swap(top[zero], top[zero - 1]);
			if (!moveCnt.count(top)) {
				moveCnt[top] = nowCnt + 1;
				Q.push(top);
			} 
			swap(top[zero], top[zero - 1]);
		} 
		if (zero % 3 != 0) {
			swap(top[zero], top[zero + 1]);
			if (!moveCnt.count(top)) {
				moveCnt[top] = nowCnt + 1;
				Q.push(top);
			} 
			swap(top[zero], top[zero + 1]);
		} 
	} 
} 

int main() {
	for (int i = 1; i <= 9; ++i)
		scanf("%d", &beg[i]);
	for (int i = 1; i <= 9; ++i)
		scanf("%d", &fin[i]);
	BFS(); 
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值