AcWing 180. 排书
原题链接
中等
作者:
ITNXD
,
2021-04-24 19:02:09
,
所有人可见
,
阅读 363
详细请查看注释
参考代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 20;
int T, n, a[N], backup[5][N];
/*
迭代加深 + IDA*
状态数量:若区间长度为i,有 n − i + 1 种选法,有 n - i 种放法!(去掉本身的放法)
将某一段向前移动,等价于将跳过的那段向后移动!因此可以除2只计算一遍!
因此最终转态数量为:(n - i) * (n - i + 1) / 2 (i可取1,2,3...n-1) ---> n最大15,因此每一步最大为560
四步内解决:因此为 560^4
可以使用双向BFS或IDA*优化!
估计函数:由于每次移动一个区间会有三个点的后继元素发生改变!(区间左右端点即插入点三点的后继会变化)
因此:统计当前状态后继位置不正确数量,则当前状态最少可以操作 tot / 3 上取整即可恢复有序!
所谓不正确后继:即当前元素值与上一个元素值加一不一致!
若当前步数 + 估价函数计算得到的最少步数 大于深度(步数)上限直接return
*/
// 启发式函数(估计函数)
// 统计前后错误连接的数量,则当前状态最多需要 (cnt + 2) / 3 (即cnt / 3 上取整)次操作即可!
int f(){
int cnt = 0;
for(int i = 0; i + 1 < n; i ++)
if(a[i + 1] != a[i] + 1) cnt ++;
return (cnt + 2) / 3;
}
// 当前深度 最大深度
bool dfs(int u, int depth){
if(u + f() > 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(backup[u], a, sizeof a);
int x, y;
// 移动[r+1,k]区间到l位置
for(x = r + 1, y = l; x <= k; x ++, y ++) a[y] = backup[u][x];
// 移动[l,r]区间到k位置
for(x = l; x <= r; x ++, y ++) a[y] = backup[u][x];
if(dfs(u + 1, depth)) return true;
memcpy(a, backup[u], sizeof a);
}
}
return false;
}
int main(){
cin >> T;
while(T --){
cin >> n;
for(int i = 0; i < n; i ++) cin >> a[i];
int depth = 0;
while(depth < 5 && !dfs(0, depth)) depth ++;
if(depth == 5) cout << "5 or more" << endl;
else cout << depth << endl;
}
return 0;
}