1 条题解
-
0
算法
(IDA*)
先考虑每一步的决策数量:
当抽取长度为 的一段时,有 种抽法,对于每种抽法,有 种放法。另外,将某一段向前移动,等价于将跳过的那段向后移动,因此每种移动方式被算了两遍,所以每个状态总共的分支数量是:$\sum _{i=1}^n(n-i) * (n - i + 1) / 2 = (15 * 14 + 14 * 13 + … + 2 * 1) / 2 = 560$。考虑在四步以内解决,最多有 个状态,会超时。
可以使用双向BFS或者IDA*来优化。我们用IDA*来解决此题。
估价函数:- 估价函数需要满足:不大于实际步数
- 在最终状态下,每本书后面的书的编号应该比当前书多1。
- 每次移动最多会断开三个相连的位置,再重新加入三个相连的位置,因此最多会将3个错误的连接修正,所以如果当前有 个连接,那么最少需要 次操作。
- 因此当前状态 的估价函数可以设计成 。
如果当前层数加上 大于迭代加深的层数上限,则直接从当前分支回溯。
时间复杂度
理论上最多搜索 个状态,使用IDA*后实际搜索的状态数量很少。
参考文献
本题解参考《算法竞赛进阶指南》 0x28 IDA* 一节。
C++ 代码
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 15; int n; int q[N], w[5][N]; int f() { int res = 0; for (int i = 0; i + 1 < n; i ++ ) if (q[i + 1] != q[i] + 1) res ++ ; return (res + 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 l = 0; l < n; l ++ ) for (int r = l; r < n; r ++ ) 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; }
- 1
信息
- ID
- 125
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 2
- 上传者