【算法竞赛入门经典】7.6 迭代加深搜索与IDA* 例题7-10 UVa11211
例题UVa11211
You have n equal-length paragraphs numbered 1 to n. Now you want to arrange them in the order of 1,2,…,n. With the help of a clipboard, you can easily do this: Ctrl-X (cut) and Ctrl-V (paste) several times. You cannot cut twice before pasting, but you can cut several contiguous paragraphs at the same time - they’ll be pasted in order.
For example, in order to make {2, 4, 1, 5, 3, 6}, you can cut 1 and paste before 2, then cut 3 and paste before 4. As another example, one copy and paste is enough for {3, 4, 5, 1, 2}. There are two ways to do so: cut {3, 4, 5} and paste after {1, 2}, or cut {1, 2} and paste before {3, 4, 5}.
Input
The input consists of at most 20 test cases. Each case begins with a line containing a single integer n (1 < n < 10), thenumber of paragraphs. The next line contains a permutation of 1,2,3,…,n. The last case is followed by a single zero, which should not be processed.
Output
For each test case, print the case number and the minimal number of cut/paste operations.
Sample Input
6
2 4 1 5 3 6
5
3 4 5 1 2
0
Sample Output
Case 1: 2
Case 2: 1
分析。
其实本题十分适合直接使用BFS。本质上就是一个BFS求不同状态之间的最短路径问题嘛。但是,可以用来当做迭代加深搜索和IDA*搜索的例题来看。
迭代加深搜索 & IDA*
迭代加深搜索(Iterative Deepening Depth-First Search, IDDFS)经常用于理论上解答树深度上没有上界的问题,这类问题通常要求出满足某些条件时的解即可。比如在“埃及分数”问题中要求将一个分数a/b分解成为若干个形如1/d的加数之和,而且加数越少越好,如果加数个数相同,那么最小的分数越大越好。下面总结一下该方法的一般流程:
概述:迭代加深搜索是通过限制每次dfs的最大深度进行的搜索。令maxd表示最大的搜索深度,那么dfs就只能在0~maxd之间来进行,如果在这个范围内找到了解,就退出大循环,否则maxd++,扩大搜索范围。但可想而知,倘若没有高效及时的退出无解的情况,那么时间上的开销也是会比较大的。这时就需要进行“剪枝”操作,及时地判断此时能否找到解。对于迭代加深搜索,经常通过设计合适的“乐观估价函数”来判断能否剪枝。设当前搜索的深度是cur,乐观估价函数是h(),那么当cur+h()>maxd时就需要剪枝。
那么什么是乐观估价函数呢?简单的说就是从当前深度到找到最终的解“至少”还需要多少步,或者距离找到最终的解还需要扩展多少层。如果超出了当前限制的深度maxd,说明当前限制的最大深度下是不可能找到解的,直接退出。比如像前面的“埃及分数”问题,要拆分19/45这样的一个分数,假设当前搜索到了第三层,得到19/45=1/5+1/100…那么根据题意此时最大的分数为1/101,而且如果需要凑够19/45,需要(19/45-1/5-1/100)*101=23个1/101才行。即从第3层还要向下扩展至少大于23层的深度才可能找到所有的解。所以如果此时的maxd<23,就可以直接剪枝了。因此(a/b-c/d)/(1/e)便是本题的乐观估价函数。
注意,使用迭代加深搜索时要保证一定可以找到解,否则会无限循环下去。
CSDN——XDU_Skyline
就是说,当宽度优先搜索数据量过于庞大甚至每一层都无上限的情况下,可以用迭代加深搜索来解决求最短路的问题。
设想宽度无限、深度无限,那求最短路的时候不能直接套用BFS也不能DFS之后求最小值。这种情况下就可以用迭代加深搜索来一层层的限定,最大层数从1往上加,一旦找到了,最大层数就是最短路。这样使用DFS来进行搜索,此时要注意寻找终止条件。即:宽度不能使随意选取的无限量,必定超过某个阈值之后就不符合条件,此时结束搜索。
另外,若是利用最大层数等等,得出剩余至少搜索数量,判断出已经无法满足要求的时候,就果断剪枝,这实际上就叫IDA*搜索。即:当前搜索层数条件加上剩余最小搜索成功条件比MaxDepth条件还要大的时候,就不可能得到有解的情况。
本题储存结构
利用a数组记录每组数据。
利用op保存每组数据的备份以便恢复
利用bo数组保存在确定剪切范围为[i,j]的情况下,剩余数据有哪些。
这样,就枚举所有可能的[i,j]下,将其插到剩余数据的任意位置。插入完成后,判断是否成功或及进行下一层搜索。
样例实现代码
#include<iostream>
#include<cstring>
#define max 9
using namespace std;
int a[max], n;
int h() {
int cnt = 0;
for (int i = 0; i < n - 1; i++)
if (a[i] + 1 != a[i + 1])
cnt++;
if (a[n - 1] != n)
cnt++;
return cnt;
}
bool is_solved() {
for (int i = 0; i < n; i++)
if (a[i] != i + 1)
return false;
return true;
}
bool dfs(int d, int dmax) {
if ((dmax - d) * 3 < h())
return false;
if (is_solved())
return true;
int op[max], bo[max];
memcpy(op, a, sizeof(a));
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
int cnt1 = 0;
for (int k = 0; k < n; k++) {
if (k < i || k>j)
bo[cnt1++] = a[k];
}
for (int k = 0; k <= cnt1; k++) {
int cnt2 = 0;
for (int p = 0; p < k; p++) {
a[cnt2++] = bo[p];
}
for (int p = i; p <= j; p++) {
a[cnt2++] = op[p];
}
for (int p = k; p < cnt1; p++) {
a[cnt2++] = bo[p];
}
if (dfs(d + 1, dmax))
return true;
memcpy(a, op, sizeof(a));
}
}
}
return false;
}
int sol() {
if (is_solved())
return 0;
for (int i = 1; i <= n; i++)
if (dfs(0, i))
return i;
}
int main() {
int kase = 1, ans;
while (cin >> n && n) {
for (int i = 0; i < n; i++)
cin >> a[i];
cout << "Case " << kase++ << ": " << sol() << endl;
}
return 0;
}
结果
TIP
迭代加深搜索的最典型例题是埃及分数问题