给定 n 本书,编号为 1∼n。
在初始状态下,书是任意排列的。
在每一次操作中,可以抽取其中连续的一段,再把这段插入到其他某个位置。
我们的目标状态是把书按照 1∼n 的顺序依次排列。
求最少需要多少次操作。
输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组数据包含两行,第一行为整数 n,表示书的数量。
第二行为 n 个整数,表示 1∼n 的一种任意排列。
同行数之间用空格隔开。
输出格式
每组数据输出一个最少操作次数。
如果最少操作次数大于或等于 5 次,则输出 5 or more
。
每个结果占一行。
数据范围
1≤n≤15
输入样例:
3
6
1 3 4 6 2 5
5
5 4 3 2 1
10
6 8 5 3 4 7 2 9 1 10
输出样例:
2
3
5 or more
代码:
/*
IDA*这个算法的思想其实比A*要好理解很多
简单来说就是我们开一个估计函数这个估计函数的返回值一定比真实值要小于等于这样我们就可以利用这个进行最优化减枝
如果估计函数都无法完成那么真实值一定也无法完成可以直接剪掉
这题由于步数很小答案可能会很大所以我们再用一下之前讲的迭代加深的思想来优化一下搜索
这题我们搜索可以去枚举我们选择的长度以及我们放的位置去搜索
这题我们其实可以不用往前放因为我们往前放会对应前面的一种选法完全没有必要
这题由于每次我们最多改变3个位置的关系所以假设我们有k个错误前后关系那么(k+2)/3就是我们最多要操作的次数
可以看到估计函数的影子了所以我们这题再用IDA*来优化
我们可以用这个(k+2)/3来当成估计函数
*/
#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 = 1; i < n; i++)
{
if (q[i] != q[i - 1] + 1)
cnt++;
}
return (cnt + 2) / 3; // 除以三上取整
}
bool dfs(int u, int depth)
{
if (f() == 0) // 不需要改 则成功
return true;
if (u + f() > depth) // 估价函数剪枝
return false;
for (int len = 1; len <= n - 1; len++)
{
for (int i = 0; i + len - 1 < n; i++)
{
int j = i + len - 1;
for (int k = j + 1; k < n; k++)
{
memcpy(w[u], q, sizeof(q));
int x, y;
for (x = j + 1, y = i; x <= k; x++, y++)
q[y] = w[u][x];
for (x = i; x <= j; x++, y++)
q[y] = w[u][x];
if (dfs(u + 1, depth))
return true;
memcpy(q, w[u], 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)) //0变到1,表示操作一次,变到2,表示操作两次,依次类推。
depth++;
if (depth >= 5)
puts("5 or more");
else
cout << depth << endl;
}
return 0;
}