由于编辑的特殊性,只要没达到升序这个条件就可以永远不停的搜下去,最特别的还是那些的错误的路径,也就是说假如一开始就选择了错误的剪切方式,之后这条错误的路会一直往后延伸。。。
所以采用IDA*解法,IDA*相比与普通的dfs或bfs,多了一个估值函数,比如我从这一层到第n层,每层最多能干多少活,假如剩下的活到第n层怎么都做不完,就不要往下搜了。像这道题,每层最多有三个数字的后缀改变,也就是说最多能使三个数字的后继正确。
/* 重要的注意事项: flag必须是全局变量:要对全部的回溯步骤造成影响 m数组必须是临时变量:只存这次,不影响上次 */ #include <iostream> #include <cstdio> #include <cstring> using namespace std; int a[11],n; inline void cat(int *c,int j,int i,int k) { int v[11],top=0; for(int x=0;x<n;x++) { if(x==k) { for(int y=j;y<=j+i;y++) { v[top++]=c[y]; } } if(x>=j&&x<=j+i) continue; v[top++]=c[x]; } memcpy(c,v,sizeof(int)*n); } int flag; //终止回溯标志 bool dfs(int cur,int depth,int *c) { if(flag==1) return false; if(flag==2) return true; int h=0,fi=0; for(int i=0;i<n;i++) { fi=0; for(int j=i+1;j<n;j++) { if(c[j]<c[i]) {fi=1;break;} } if(fi) h++; } if(h+3*cur>3*depth) return false; //估值剪枝,cur亦可代表已完成的层数 if(cur==depth) { int ff=0; for(int i=0;i<n;i++) { for(int j=i+1;j<n;j++) { if(c[j]<c[i]) {ff=1;break;} } if(ff) break; } flag=1; if(!ff) {flag=2;return true;} return false; } int m[11]; //存储原态数组 memcpy(m,c,sizeof(int)*n); for(int i=0;i<=n-1;i++) { for(int j=1;j<=n-i;j++) { for(int k=0;k<j;k++) { if(j+i>=n) break; cat(c,j,i,k); if(dfs(cur+1,depth,c)) return true; memcpy(c,m,sizeof(int)*n); } } } return false; } int main() { int ca=0; for(;scanf("%d",&n),n!=0;) { for(int i=0;i<n;i++) { scanf("%d",&a[i]); } int ans=0; for(int i=0;;i++) { flag=0; if(dfs(0,i,a)) {ans=i;break;} } printf("Case %d: %d ",++ca,ans); } return 0; }