题意:有n (n<=15) 本高度不同的书,每次可以交换相邻的两部分书(多本),问最少多少次操作可以让书由小到大排列?如果次数>=5 输出5 or more,否则输出最少次数。
分析:n!的状态空间太大,不能广搜,于是想到迭代加深搜索,又因为每次操作,最多改变3个后继值,最终状态每本书的后继都比自身大1,可以设计启发函数为 错误的后继值/3.
另外在回溯的时候,假设本次交换的区间为[1,5] [6,8]那么还原状态应该交换[1,3] [4,8],而不能再次交换[1,5] [6,8]。。
1 2 3 4 5|6 7 8
交换[1,5] [6,8]: 6 7 8|1 2 3 4 5
交换[1,3] [4,8]: 1 2 3 4 5|6 7 8
int lim; int n,ans; int a[20]; inline int h(){ int cnt=0; FOR(i,0,n-1) if(a[i]+1!=a[i+1]) cnt++; return (cnt+2)/3; } //void print(){ FOR(i,0,n)cout<<a[i]<<' ';cout<<endl; } inline void Swap(int x,int y,int p,int q){ //if(lim==2) {cout<<x<<' '<<y<<' '<<p<<' '<<q<<' '<<lim<<' '<<endl;print();} int t1=(y-x+1)>>1, t2=(q-p+1)>>1, t3=(q-x+1)>>1; FOR(i,0,t1){ swap(a[x+i],a[y-i]); } FOR(i,0,t2){ swap(a[p+i],a[q-i]); } FOR(i,0,t3){ swap(a[x+i],a[q-i]); } //if(lim==2){ print();cout<<endl;} } void IDA_Star(int s){ int H=h(); //if(lim==2) cout<<i<<' '<<H<<' '<<lim<<endl; if(H==0){ans=s;return;} else if(s+H>lim)return; FOR(i,0,n-1) FOR(j,i+1,n) FOR(k,i,j){ if(ans!=-1)return; Swap(i,k,k+1,j); IDA_Star(s+1); Swap(i,j-k+i-1,j-k+i,j); } } int main(){ #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); #endif int T; cin>>T; while(T--){ cin>>n; FOR(i,0,n)cin>>a[i]; ans=-1; lim=h(); while(lim<=4 && ans==-1){ IDA_Star(0); lim++; } if(ans==-1)cout<<"5 or more"<<endl; else cout<<ans<<endl; } return 0; }