题意:
有N(1 ≤ N ≤ 15)本书,每本与每本的高度都不一样。现在可以按照以下的办法整理书:抽出一摞书,再保持原来的顺序插进一个位置。
这样的话我们称之为“一次操作”。现在你需要求出至少需要经过几次操作才能让书变成按高度升序的状态。如果需要5次或者多于5次,只需要输出“5 or more”。
思路:
1. 题目中已经有个很明显的提示:最多不超过 5 次。于是便可以用 IDA* + 暴力枚举试图去解决问题;
2. IDA* 不难,暴力也不难,难的是如果根据状态设计启发式函数:由于每次操作,最多能改变 3 个元素的后继,所以根据这个信息点来设计启发式函数能满足要求;
#include <iostream>
#include <algorithm>
using namespace std;
int books[20], N;
void swapbooks(int p, int x, int q) {
int buffer[20], c = 0;
for (int i = x + 1; i <= q; i++)
buffer[c++] = books[i];
for (int i = p; i <= x; i++)
buffer[c++] = books[i];
for (int i = p; i <= q; i++)
books[i] = buffer[i-p];
}
int getdiff() {
int h = 0;
for (int i = 0; i < N - 1; i++) {
if (books[i] + 1 != books[i+1])
h += 1;
}
if (books[N-1] != N)
h += 1;
return (h + 2) / 3;
}
int maxdepth;
bool succ;
int dfs(int depth) {
int h = getdiff();
if (h == 0) {
succ = true;
return depth;
}
int f = depth + h;
if (f > maxdepth)
return f;
int newbound = 1e9;
for (int p = 1; p < N; p++) {
for (int i = 0, j = i + p; j < N; i++, j++) {
for (int k = i; k < j; k++) {
swapbooks(i, k, j);
int f = dfs(depth + 1);
if (succ)
return f;
newbound = min(f, newbound);
swapbooks(i, i + j - k - 1, j);
}
}
}
return newbound;
}
int main() {
int cases;
scanf("%d", &cases);
while (cases--) {
scanf("%d", &N);
for (int i = 0; i < N; i++)
scanf("%d", &books[i]);
maxdepth = getdiff();
if (maxdepth)
succ = false;
else
succ = true;
while (!succ && maxdepth < 5)
maxdepth = dfs(0);
if (!succ)
printf("5 or more\n");
else
printf("%d\n", maxdepth);
}
return 0;
}