八数码Ⅱ

该程序实现了一个用C++编写的A*和IDA*算法来解决八数码问题。估价函数基于曼哈顿距离,同时进行了忽略无效操作的优化。通过深度优先搜索寻找解,直到达到目标步数或找到解决方案。

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

    这一题跟八数码的区别就是一个是A*算法一个是IDA*算法,本质都是差不多的,但是还有一些细节需要去考虑。

        1、估价函数:跟八数码一眼的,只需要知道每个符号最终的位置就行了,所以首先预处理一下每个字符的位置。然后在查找某个状态的估价值的时候,就是只需要找到0~8的所有数字到达最终位置的曼哈顿距离就行,这些曼哈顿距离的和就是估价值,0~8位置正确,那么X肯定是正确的位置。

        2、优化:每个位置有四个方向,其中有一个是来当前位置的方向,要是回去就相当于是无效操作,那么则可以进行忽略无效操作的优化。

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

const int N = 1e3;
typedef long long ll;
typedef pair<int, int> pii;
int maxi, idx, dx[] = {1, 0, 0, -1}, dy[] = {0, -1, 1, 0}; 
string ed, be, p = "dlru";// 方向坐标是按字典序的,这样可以使找到的最小步数的字典序也是同步数最小的情况。
pii o[300];
char str[N];
int f(string a) // 估价函数,就是每个字符位置跟最终位置的曼哈顿距离,也就是当前字符要走到最终位置需要的最小步数。
{
    int s = 0;
    for (int i = 0; i < 9; i ++)
    if(a[i] != 'X') // 如果0~8都正确了,那X自然也就正确了。
        s += abs(i / 3 - o[a[i]].first) + abs(i % 3 - o[a[i]].second);
    return s;
}
int pp[N]; 
bool dfs(int x)
{
	if(x + f(be) > maxi) return false; // 当前深度加上最小估价 还大于目标步数,则直接结束
	if(be == ed) return true; // 到达终点 直接结束 返回状态
	int a, b;
	for(int i = 0; i < 9; i ++) // 获取X的位置的横纵坐标
		if(be[i] == 'X')
		{
			a = i / 3, b = i % 3;
			break;
		}
	for(int i = 0; i < 4; i ++) // 枚举方向
	{
		int a1 = a + dx[i], b1 = b + dy[i]; // X移动后的坐标	
		if(x && i + pp[x - 1] == 3) continue; // 优化:当天操作相当于回到上一个位置,那就跳过
		if(a1 < 0 || a1 > 2 || b1 < 0 || b1 > 2) continue; // 超出地图 跳过
		swap(be[a * 3 + b], be[a1 * 3 + b1]); // 移动X的位置
		str[x] = p[i]; // 记录当前行进方向
		pp[x] = i; // 记录行进方向下标,用来判断是否是回到原地
		if(dfs(x + 1)) // 向更深一层搜索
			return true;
		swap(be[a * 3 + b], be[a1 * 3 + b1]); // 还原场地
	}
	return false;
}
int main(void)
{
    IOS;
    int t, g = 1;
    cin >> t;
    while (t --)
    {
        cin >> be >> ed;
        idx = 0;
        for(int i = 0; i < 9; i ++) // 预处理,保存一下每个字符最终的位置。
            o[ed[i]] = {i / 3, i % 3};
		cout << "Case " << g ++ << ": ";
        for(maxi = 0; ; maxi ++) // IDA* 思想
            if(dfs(0))
                break;
        cout << maxi << endl;
        for(int i = 0 ; i < maxi; i ++) cout << str[i];
        cout << endl;
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值