题意: 给出n( 2<=n<=9) 个乱序的数组 要求拍成升序 每次 剪切一段加上粘贴一段算一次 拍成1 2 3 4 .。。n即可 求排序次数
典型的状态空间搜索问题 初始状态为输入 结束状态为升序
分析: 因为n最大为就 排列最多为9!=362880个 虽软这个数字不是很大 但是每次剪切都可能不是一个数组 所以枚举量还要大大增加 所以肯定要优化
这里用到了迭代加深搜索: 最大次数为9 所以将次数从0开始枚举 直到首先找到一个最小的答案
迭代加深搜索其实和暴力算法没什么两样 但是重要的是找到启发函数(剪枝) 这个剪枝很重要 大大缩短时间
这题可得启发函数: d为深度 h为不正确的数组 maxx为当前次数
可证明(见紫书)每一次操作 h最多减少三 本来是 d+h<=maxx 时可以成立
现在有方程 3d+h>3maxx 时剪枝
启发函数就是和迭代加深搜索一起用 起到大量剪枝的作用
#include<bits/stdc++.h> using namespace std; #define N 9 int n,a[N];bool judge(void) {for(int i=0;i<n-1;i++)if(a[i]>=a[i+1])return 0;return 1; }int h(void) {int cnt=0;for(int i=0;i<n-1;i++)if(a[i]+1!=a[i+1])cnt++;if(a[n-1]!=n)cnt++;return cnt; }bool dfs(int d,int maxx) {if(3*d+h()>3*maxx)return false;if(judge())return true;int olda[N],b[N];memcpy(olda,a,sizeof a);for(int i=0;i<n;i++)for(int j=i;j<n;j++){int cnt2=0;for(int k=0;k<n;k++)if(k<i||k>j)b[cnt2++]=a[k];for(int k=0;k<=cnt2;k++){int cnt=0;for(int p=0;p<k;p++)a[cnt++]=b[p];for(int p=i;p<=j;p++)a[cnt++]=olda[p];for(int p=k;p<cnt2;p++)a[cnt++]=b[p];if(dfs(d+1,maxx))return 1;memcpy(a,olda,sizeof a);}}return 0; }int solve(void) {if(judge())return 0;for(int maxx=1;maxx<=8;maxx++)if (dfs(0,maxx) ) return maxx; }int main() {int cas=0;while(scanf("%d",&n),n){for(int i=0;i<n;i++)scanf("%d",&a[i]);printf("Case %d: %d\n",++cas,solve());} }