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;
}
#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
*/