引言:
上次讲到的估价函数 f 与带二叉堆的bfs结合形成了astar算法;
那么,f 函数能不能与dfs结合?
dfs是深度优先搜索,即一直向一个方向搜索,搜索完毕就返回;
但其实这样直接与估价函数结合可能会导致出错;
一旦估价函数出错,那么我们可能向下搜索一个不会出答案的分支;
导致浪费时间,导致超时;
所以我们最终选择将估价函数与 迭代加深的dfs 相结合;
f 为每个状态到目标状态的需要的步数。
当然 f 遵循 astar 中 f 定义的 估计值不大于实际步数
深度限制回溯条件变为, 当前深度 + 估计值 > 深度限制 ,return;
这就是 IDAstar算法——迭代加深的Astar算法;
例题: booksort
题意:
给定n本书,编号为1-n,在初始状态下,书是任意排列的,在每一次操作中,可以抽取其中连续的一段,再把这段插入到其他某个位置。
我们的目标状态是把书按照1-n的顺序依次排列,求最少需要多少次操作。
若操作次数>=5,输出“5 or more”。
这道题别看他它只需要四步搜索,但它的搜索规模其实很大,粗摸估计一次搜索最坏要560;
四次就是560^4;
这个肯定无法承受;
所以我们采用idastar
定义一个玩意儿:
正确后继:指在目标状态中 i 后面应是 i+1 ,所以我们将 i+1 称为 i 的正确后继;
任意状态state;
我们设它的整个排列状况中错误后继的个数为 tot ;
我们可以发现:每次进行修改我们可以进行三个后继的修改、
设s[]为修改区间,从l~r
分别是: l-1、r、转移后的l'-1;
共有三个;
假如是理想状况下:每次都改对错误后继三个,那么我们的最小修改应是 tot/3(向上取整),所以根据我们 f 函数的定义,整个最小修改完全符合条件;
然后采用迭代加深的方法,若 当前步数+f(state)>k(当前限制),直接回溯;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 using namespace std; 5 const int N = 20; 6 int n, a[N], dep; 7 8 int gj() {//tot计算 9 int cnt = 0; 10 for (int i = 1; i < n; i++) 11 if (a[i] + 1 != a[i+1]) cnt++; 12 if (a[n] != n) return cnt + 1; 13 return cnt; 14 } 15 16 void work(int l, int r, int t) { 17 int b[N], p = r; 18 for (int i = l; i <= t; i++) { 19 b[i] = a[++p]; 20 if (p == t) p = l - 1; 21 } 22 for (int i = l; i <= t; i++) a[i] = b[i]; 23 } 24 25 bool dfs(int now) { 26 int cnt = gj(); 27 if (!cnt) return 1; 28 if (3 * now + cnt > 3 * dep) return 0; 29 int c[N]; 30 memcpy(c, a, sizeof(c)); 31 for (int l = 1; l <= n; l++) 32 for (int r = l; r <= n; r++) 33 for (int t = r + 1; t <= n; t++) { 34 work(l, r, t); 35 if (dfs(now + 1)) return 1; 36 memcpy(a, c, sizeof(a));//回溯 37 } 38 return 0; 39 } 40 41 void Booksort() { 42 cin >> n; 43 for (int i = 1; i <= n; i++) scanf("%d", &a[i]); 44 for (dep = 0; dep <= 4; dep++) 45 if (dfs(0)) { 46 cout << dep << endl; 47 return; 48 } 49 puts("5 or more"); 50 } 51 52 int main() { 53 int t; 54 cin >> t; 55 while (t--) Booksort(); 56 return 0; 57 }