POJ 2286 The Rotation Game (IDA*)

本文深入解析IDA*算法,包括迭代加深算法、A*算法的特点及其结合应用,通过POJ2286实例演示IDA*算法在问题求解中的优势。重点阐述了估价函数的选取原则与IDA*算法的实现过程,包括如何通过简单的移动步骤估算解的接近度,以及在大规模搜索问题中的应用效果。

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

以下部分摘自网络:

IDA*算法是A*算法和迭代加深算法的结合。




迭代加深算法是在dfs搜索算法的基础上逐步加深搜索的深度,它避免了广度优先搜索占用搜索空间太大的缺点,也减少了深度优先搜索的盲目性。它主要是在递归搜索函数的开头判断当前搜索的深度是否大于预定义的最大搜索深度,如果大于,就退出这一层的搜索,如果不大于,就继续进行搜索。这样最终获得的解必然是最优解。



而在A*算法中,我们通过使用合理的估价函数,然后在获得的子节点中选择fCost最小的节点进行扩展,以此完成搜索最优解的目的。但是A*算法中,需要维护关闭列表和开放列表,需要对扩展出来的节点进行检测,忽略已经进入到关闭列表中的节点(也就是所谓的“已经检测过的节点”),另外也要检测是否与待扩展的节点重复,如果重复进行相应的更新操作。所以A*算法主要的代价花在了状态检测和选择代价最小节点的排序上,这个过程中占用的内存是比较大的,一般为了提高效率都是使用hash进行重复状态检测。



而IDA*算法具有如下的特点:

综合了A*算法的人工智能性和回溯法对空间的消耗较少的优点,在一些规模很大的搜索问题中会起意想不到的效果。它的具体名称是 Iterative Deepening A*, 1985年由Korf提出。该算法的最初目的是为了利用深度搜索的优势解决广度A*的空间问题,其代价是会产生重复搜索。归纳一下,IDA*的基本思路是:首先将初始状态结点的H值设为阈值maxH,然后进行深度优先搜索,搜索过程中忽略所有H值大于maxH的结点;如果没有找到解,则加大阈值maxH,再重复上述搜索,直到找到一个解。在保证H值的计算满足A*算法的要求下,可以证明找到的这个解一定是最优解。在程序实现上,IDA* 要比 A* 方便,因为不需要保存结点,不需要判重复,也不需要根据 H值对结点排序,占用空间小。
而这里在IDA*算法中也使用合适的估价函数,来评估与目标状态的距离。

在一般的问题中是这样使用IDA*算法的,当前局面的估价函数值+当前的搜索深度 > 预定义的最大搜索深度时,就进行剪枝。

这个估计函数的选取没有统一的标准,找到合适的该函数并不容易,但是可以大致按照这个原则:在一定范围内加大各个状态启发函数值的差别。

/*********************************************************************************************************************************************************/


POJ 2286

估价函数:v = 8 - max(max(tmp[0],tmp[1]),tmp[2]); tmp值分别存储的是中间8个格子中1,2,3的的数量,所以最好的情况也要移动v步才能得到答案

IDA*的内涵有待慢慢研究...............................

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <climits>//形如INT_MAX一类的
#define MAX 100005
#define INF 0x7FFFFFFF
#define REP(i,s,t) for(int i=(s);i<=(t);++i)
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define mp(a,b) make_pair(a,b)
#define L(x) x<<1
#define R(x) x<<1|1
# define eps 1e-5
//#pragma comment(linker, "/STACK:36777216") ///传说中的外挂
using namespace std;
// map存储数组a的映射位置
int map[5][8] = {7,8,9,12,13,16,17,18, //中心八格
                 1,3,7,12,16,21,23,0,  //第一竖
                 2,4,9,13,18,22,24,0,  //第二竖
                 11,10,9,8,7,6,5,0,    //第一行逆序
                 20,19,18,17,16,15,14,0//第二行逆序
                };
int buff[111],limit,ok,ans;
int change[9] = {0,1,2,3,4,6,5,8,7}; // 相反的移动值相差4
int h(int a[]) {
    int tmp[3] = {0,0,0};
    for(int i=0; i<8; i++) {
        tmp[a[map[0][i]] - 1] ++;
    }
    return 8 - max(max(tmp[0],tmp[1]),tmp[2]);
}

void move(int a[],int kind) {
    if(kind <= 4) {
        a[map[kind][7]] = a[map[kind][0]];
        for(int i=1; i<7; i++) {
            a[map[kind][i-1]] = a[map[kind][i]];
        }
        a[map[kind][6]] = a[map[kind][7]];
    } else {
        kind -= 4;
        a[map[kind][7]] = a[map[kind][6]];
        for(int i=6; i>=0; i--) {
            a[map[kind][i]] = a[map[kind][i-1]];
        }
        a[map[kind][0]] = a[map[kind][7]];
    }
}
int dfs(int a[],int step,int pre) {
    int es = h(a);
    if(es == 0) { //达到目标情况
        ok = 1;
        ans = a[7];
        return step;
    }
    if(step + es > limit) return step + es; //当前估价值+已走步数>一开始的估价值
    int minn = INF;
    for(int i=1; i<=8; i++) {
        if(abs(change[i] - pre) == 4) continue; //两次移动刚好相反,回到两步前的状态
        buff[step] = i;
        int tmp[30];
        for(int j=1; j<=24; j++) tmp[j] = a[j];
        move(tmp,change[i]);
        int up = dfs(tmp,step + 1,change[i]);
        minn = min(minn,up);
        if(ok) return up;
    }
    return minn;
}

void IDA_STAR(int a[]) {
    ok = 0;
    memset(buff,0,sizeof(buff));
    while(ok == 0) {
        limit = dfs(a,0,-10);
    }
    for(int i=0; i<limit; i++) printf("%c",buff[i] + 'A' - 1);
    puts("");
    //printf("limit: %d\n",limit);
    printf("%d\n",ans);
}

int main() {
    int a[30];
    while(scanf("%d",&a[1]) && a[1]) {
        for(int i=2; i<=24; i++) scanf("%d",&a[i]);
        limit = h(a);
        if(limit == 0) {
            printf("No moves needed\n");
            printf("%d\n",a[7]);
            continue;
        }
        IDA_STAR(a);
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值