IDA*算法

本文介绍了IDA*算法在解决实际问题中的应用,包括ACwing180题的图书排序问题和ACwing181题的回转游戏。IDA*算法结合了迭代加深和全局最优剪枝,减少了空间需求且不需要排序和判重。在图书排序问题中,通过估价函数计算最少操作次数;在回转游戏中,通过估价函数评估回转次数。两种问题都展示了IDA*算法的有效性和效率。
摘要由CSDN通过智能技术生成

IDA*算法是迭代加深的A*算法,同时运用了迭代加深全局性最优剪枝,与A*算法比起来,具有以下优点:
(1).不需要判重,不需要排序
(2).空间需求减少
基本思路:
首先将初始状态结点的H值设为阈值maxH,然后进行深度优先搜索,搜索过程中忽略所有H值大于maxH的结点;如果没有找到解,则加大阈值maxH,再重复上述搜索,直到找到一个解。在保证H值的计算满足A*算法的要求下,可以证明找到的这个解一定是最优解。而这里在IDA*算法中也使用合适的估价函数,来评估与目标状态的距离。
在一般的问题中是这样使用IDA*算法的,当前局面的估价函数值+当前的搜索深度 > 预定义的最大搜索深度时,就进行剪枝。
IDA*算法基本框架:

bool dfs(int depth,int max_depth){
    if(depth+f()>max_depth) return false;
    if(check()) return true;
    /*搜索每个分支
   		......
    */
    return false;
}
int main()
{
 
     int depth=0;
     while(!dfs(0,depth)) depth++;//逐步增加搜索的最大层数上限
        
    return 0;
}

例题

acwing180.排书
题意:
给定 n 本书,编号为 1∼n。在初始状态下,书是任意排列的。在每一次操作中,可以抽取其中连续的一段,再把这段插入到其他某个位置,我们的目标状态是把书按照1∼n 的顺序依次排列,求最少需要多少次操作。
思路:
运用IDA*算法,因为每进行一次操作,最多可以使3个无序连接点排序,当序列有序时应满足q[i]==q[i+1]-1,因此可以设tot为当前书的编号序列中q[i]!=q[i+1]-1的连接点,估价函数设为 f ( s ) = ⌈ t o t / 3 ⌉ f(s)=\left \lceil tot/3 \right \rceil f(s)=tot/3
如果当前层数加 f ( s ) f(s) f(s)大于预定义的层数上限,则直接从当前分支回溯

#include<iostream>
#include<algorithm>
#include<queue>
#include<set>
#include<stdlib.h>
#include<time.h>
#include<unordered_map>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<string>
#include<map>
#include<cmath>
#include<bitset>
#define ll long long
#define inf 0x3f3f3f3f
#define bug(a) cout<<"* "<<a<<endl;
#define bugg(a,b) cout<<"* "<<a<<" "<<b<<endl;
#define buggg(a,b,c) cout<<"* "<<a<<" "<<b<<" "<<c<<endl;
using namespace std;
typedef pair<int,int> P;
const int N=15;
const ll mod=1e9+7;

int n;
int q[N];
int w[5][N];
int f(){
    int tot=0;
    for(int i=0;i<n-1;i++){
        if(q[i+1]!=q[i]+1)
            tot++;
    }
    return (tot+2)/3;
}
bool check(){
    for(int i=0;i<n;i++){
        if(q[i]!=i+1)
            return false;
    }
    return true;
}
bool dfs(int depth,int max_depth){
    if(depth+f()>max_depth) return false;//当前状态已操作次数加预估还需操作的次数大于规定的最大的操作次数
    if(check()) return true;
    //搜索每个分支
    for(int len=1;len<=n;len++){
        for(int l=0;l+len-1<n;l++){
            int r=l+len-1;
            for(int k=r+1;k<n;k++){
                memcpy(w[depth],q,sizeof(q));
                int x,y;
                //如果往前移动会有重复操作,所以只进行往后移动即可
                for(x=r+1,y=l;x<=k;x++,y++)
                    q[y]=w[depth][x];
                for(x=l;x<=r;x++,y++)
                    q[y]=w[depth][x];
                if(dfs(depth+1,max_depth)) return true;
                memcpy(q,w[depth],sizeof(q));
            }
        }
    }
    return false;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%d",&q[i]);
        int depth=0;
        while(depth<5&&!dfs(0,depth)) depth++;
        if(depth>=5)
            puts("5 or more");
        else
            printf("%d\n",depth);
    }
    return 0;
}

acwing181.回转游戏

#include<iostream>
#include<algorithm>
#include<queue>
#include<set>
#include<stdlib.h>
#include<time.h>
#include<unordered_map>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<string>
#include<map>
#include<cmath>
#include<bitset>
#define ll long long
#define inf 0x3f3f3f3f
#define bug(a) cout<<"* "<<a<<endl;
#define bugg(a,b) cout<<"* "<<a<<" "<<b<<endl;
#define buggg(a,b,c) cout<<"* "<<a<<" "<<b<<" "<<c<<endl;
using namespace std;
typedef pair<int,int> P;
const int N=24;
const ll mod=1e9+7;

int op[8][7]={
0,2,6,11,15,20,22,
1,3,8,12,17,21,23,
10,9,8,7,6,5,4,
19,18,17,16,15,14,13,
23,21,17,12,8,3,1,
22,20,15,11,6,2,0,
13,14,15,16,17,18,19,
4,5,6,7,8,9,10
};
int q[N];
int center[8]={6,7,8,11,12,15,16,17};
int opsite[8]={5,4,7,6,1,0,3,2};
int path[100];
void operate(int x){
    int tmp=q[op[x][0]];
    for(int i=0;i<6;i++)
        q[op[x][i]]=q[op[x][i+1]];
    q[op[x][6]]=tmp;
}

int f(){//估价函数:f(s)=8-出现次数最多的数字出现的次数
    int sum[4]={0},res=0;
    for(int i=0;i<8;i++)
        sum[q[center[i]]]++;
    for(int i=1;i<=3;i++)
        res=max(sum[i],res);
    return 8-res;
}
bool check(){
    for(int i=0;i<8;i++)
        if(q[center[i]]!=q[6])
            return false;
    return true;
}

bool dfs(int depth,int max_depth,int last){
    if(depth+f()>max_depth) return false;
    if(check()) return true;

    for(int i=0;i<8;i++){
        if(last==opsite[i]) continue;
        operate(i);
        path[depth]=i;
        if(dfs(depth+1,max_depth,i)) return true;
        operate(opsite[i]);
    }
    return false;
}

int main()
{
    while(~scanf("%d",&q[0]),q[0]){
        for(int i=1;i<N;i++)
            scanf("%d",&q[i]);
        int depth=0;
        while(!dfs(0,depth,-1)) depth++;
        if(!depth)
            printf("No moves needed");
        else
            for(int i=0;i<depth;i++)
                printf("%c",path[i]+'A');
        cout<<endl;
        printf("%d\n",q[6]);

    }
    return 0;
}

/*
      0     1
      2     3
4  5  6  7  8   9 10
      11    12
13 14 15 16 17 18 19
      20    21
      22    23
*/



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值