给定 \(n\) 本书,编号为 1~n。
在初始状态下,书是任意排列的。
在每一次操作中,可以抽取其中连续的一段,再把这段插入到其他某个位置。
我们的目标状态是把书按照 1~n 的顺序依次排列。
求最少需要多少次操作。
解:
考虑每一步决策数量,当抽取长度为 \(l\) 的一段时,有 \(n - l + 1\) 种抽法,对于每种抽法,有 \(n - l\) 种放法。另外,将某一段向前移动,等价于将跳过的那段向后移动,因此每种移动方式被算了两遍,所以每个状态总共的分支数量是:
\[\sum\limits_{l = 1}^n \cfrac{(n - l + 1) * (n - l)}{2} = \cfrac{15*14 + 14*13 + 13*12...+2*1}{2} = 560
\]
题目要求最多\(4\)次算出答案,那么就是\(560^4\),又由于\(IDA \,\, star\) 有估价函数,那么实际上数据量会更小。
考虑\(IDA star\)与\(A star\)。
\(IDA star\)是迭代加深和估价函数结合版
// Problem: 排书
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/182/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
const int N = 15;
int n;
int q[N];
int w[5][N];
int f() {
int cnt = 0;
for (int i = 0; i + 1 < n; i++) {
if (q[i + 1] != q[i] + 1) cnt++;
}
return (cnt + 2) / 3;
}
bool dfs(int depth, int max_depth) {
if (depth + f() > max_depth) return false;
if (f() == 0) 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 y = l;
for (int x = r + 1; x <= k; x++, y++) q[y] = w[depth][x];
for (int 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;
cin >> t;
while (t--) {
cin >> n;
for (int i = 0; i < n; i++) cin >> q[i];
int depth = 0;
while (depth < 5 && !dfs(0, depth)) depth++;
if (depth == 5) puts("5 or more");
else cout << depth << endl;
}
return 0;
}
\(A star\)
注意:
在哈希表中嵌套结构体的时候需要重载相应的函数,例如哈希函数,所以这里不如直接自己实现哈希函数。
还要注意堆中嵌套结构体重载一下小于号的方式。
// Problem: 排书
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/182/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int N = 15;
int n, P = 17;
int w[5][N];
struct Node {
int q[N], step, f;
bool operator < (const Node &x) const{
return f > x.f;
}
}start;
//在unordered_set中嵌套结构体还是要重写Hash函数,不如直接这样写
ULL getHash(Node x) {
ULL res = 0;
for (int i = 0; i < n; i++) {
res = res * P + x.q[i];
}
return res;
}
int f(Node x) {
int cnt = 0;
for (int i = 0; i + 1 < n; i++) {
if (x.q[i + 1] != x.q[i] + 1) cnt++;
}
return (cnt + 2) / 3;
}
int astar() {
unordered_set<ULL> st;
priority_queue<Node> heap;
start.step = 0, start.f = f(start);
heap.push(start); st.insert(getHash(start));
while (heap.size()) {
auto t = heap.top();
heap.pop();
if (t.f >= 5) return 5;
if (f(t) == 0) return t.step;
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++) {
Node v;
for (int d = 0; d < n; d++) v.q[d] = t.q[d];
int y = l;
for (int x = r + 1; x <= k; x++, y++) v.q[x] = t.q[y];
for (int x = l; x <= r; x++, y++) v.q[x] = t.q[y];
if (st.count(getHash(v))) continue;
st.insert(getHash(v));
v.step = t.step + 1;
v.f = v.step + f(v);
heap.push(v);
}
}
}
}
return 5;
}
int main() {
int t;
cin >> t;
while (t--) {
cin >> n;
for (int i = 0; i < n; i++) cin >> start.q[i];
int res = astar();
if (res >= 5) puts("5 or more");
else cout << res << endl;
}
return 0;
}