POJ3460 Booksort
题意:给定一个长度为n的序列,每次可以取出其中的一段数,插入任意一个位置,问最少需要几次操作才能使整个序列变为1~n
- 思路:IDA*+迭代加深搜索
- 小技巧:将一段数插入到另一段,等价于交换相邻的两端
- 估价函数:每次操作最多改变三个数的后继,统计错误后继的个数再/3即为最少需要的步数
- 代码:
#include <cstdio> #include <iostream> #include <algorithm> #include <cctype> #include <cmath> #include <cstring> inline int read() { int x(0),f(1); char ch; while(!isdigit(ch=getchar())) if(ch=='-') f=-1; while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); return f*x; } #define res register int const int N=30; int a[N],n; inline int f() { int tmp(0); for(res i=1 ; i<=n-1 ; i++) if(a[i+1]!=a[i]+1) tmp++; if(a[n]!=n) tmp++; return tmp; } //将一段插入到另一段等价于交换相邻的两段 inline void move(int x1,int x2,int x3) { int b[N],j=0; for(res i=x2+1 ; i<=x3 ; i++) b[++j]=a[i]; for(res i=x1 ; i<=x2 ; i++) b[++j]=a[i]; for(res i=x1,k=1 ; i<=x3 ; i++,k++) a[i]=b[k]; } int dfs(int depth,int max_dep) { for(res x1=1 ; x1<=n-1 ; x1++) for(res x2=x1 ; x2<=n-1 ; x2++) for(res x3=x2+1 ; x3<=n ; x3++) { move(x1,x2,x3); int h=f(); if(!h) return 1; else if(depth+(int)ceil((double)h/3)<=max_dep) { if(dfs(depth+1,max_dep)) return 1; } move(x1,x3-(x2-x1)-1,x3);//还原 } return 0; } int main() { int T=read(); while(T--) { n=read(); for(res i=1 ; i<=n ; i++) a[i]=read(); if(f()==0) { puts("0"); continue; } for(res max_dep=1 ; max_dep<=4 ; max_dep++) { if(dfs(1,max_dep)) { printf("%d ",max_dep); break; } if(max_dep==4) puts("5 or more"); } } return 0; }