八数码问题题解

本蒟蒻的第二篇题解

题目传送门

首先,看到这样状态变化并且要求出最小步数的问题,最便捷的方法就是搜索。考虑到时间复杂度,我们首选bfs。

在bfs中,比较重要的一个环节是判定状态是否已经被访问过。这里的棋盘如果用二维数组来存储,不论是访问还是标记都不是很方便。而题目的输入格式给了我们一些提示,因此,我们使用一个字符串来存储棋盘的状态。对于是否被访问的判定,我们可以选用一个map来解决,这是目前我认为最简单的方法。

这里每一次状态的变化都在字符串中完成,就不像普通的二维数组,可以直接模拟上下左右四个位置的变换,但是,我们可以从题目中所给的图中发现,0与它上下左右四个数的位置关系,在字符串下标中的体现是{-3,3,-1,1},最后在判定每次操作是否合法的时候,方法如下:

int dir[4] = {-3, 3, 1, -1};
bool in(int x) {
    return x >= 0 && x < 9;
}
if ((in(tx) && i <= 1) || (in(tx) && i > 1 && x / 3 == tx / 3)) {
}

x为操作前‘0’的下标,tx为一次操作后‘0’的下标,i是dir数组中的下标,进行‘0’上下移动的时候,只需判定操作后‘0’的下标是否在0~8之间,进行‘0’左右移动的时候,还需要判定操作前后是否在棋盘的同一行。

最后附上AC代码:

#include <bits/stdc++.h>
using namespace std;
string st, sp;
map<string, int> h;
int dir[4] = {-3, 3, 1, -1};
bool flag;
bool in(int x) {
    return x >= 0 && x < 9;
}
struct node{
    string s;
    int zb, cnt;
};
void bfs(int x, int c) {
	queue<node> q;
    q.push({st, x, c});
    h[st] = 1;
    while (!q.empty()) {
        node now = q.front();
        q.pop();
        x = now.zb;
        for (int i = 0; i < 4; i++) {
            int tx = x + dir[i];
            string s3 = now.s;
            if ((in(tx) && i <= 1) || (in(tx) && i > 1 && x / 3 == tx / 3)) {
            	swap(s3[x], s3[tx]);
            	if (!h[s3]) {
					if (s3 == sp) {
	                    cout << now.cnt + 1 << endl;
	                    flag = true;
	                    return;
	                }
	                h[s3] = 1;
	                q.push({s3, tx, now.cnt + 1});
				} 
            }
        }
    }
}
int main(){
    cin >> st >> sp;
    if (st == sp) {
    	cout << 0 << endl;
		return 0; 
	}
    int sx;
    for (int i = 0; i < st.size(); i++) {
        if (st[i] == '0') sx = i;
    }
    bfs(sx, 0);
    if (!flag) cout << "impossible" << endl;
    return 0;
}

如果有大佬有更好的算法,可以在评论中提出。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值