原题:
题意:
给你n个数的任意排列,现在要你通过从中选出一段,然后插入某个位置的操作将这n个数按顺序依次排列,同时操作次数最少。
分析:
普通的dfs肯定会超时间复杂度,所以要用IDA*,也就是迭代加深+估价函数。
代码有三个函数,f()表示估价函数。因为一次移动最多可以改变三个位置的后继,所以遍历当前排列,记录不满足a[i]=a[i-1]+1的位置个数cnt。最优的情况无非就是每次移动都正好使位置点的后继满足顺序,即只需要(cnt/3)上取整次操作。
check()是用来检查当前状态是否满足顺序。
由于用到迭代加深,dfs()中的dep是用来记录深度限制的。在这里主要想模拟一下区间移动的过程,虽然可能不难理解,但是图解会清晰一些。当前要对t[dep]与a数组进行交换,a记录的是转移后的结果,其中L、R表示需要转移的区间左右端点,len表示之间的长度,k(代码中的loc)表示的是需要插入的位置。
-
首先,因为两个数组一开始都是一样的,把t[dep]复制给a。
-
转移[L,R]后,[R+1,k]前移,因此直接转移[R+1,k]至L处。
-
转移[L,R]到k位置后,其他位置不变,转移完成。
题解:
#include <bits/stdc++.h> using namespace std; const int N=20; int a[N],t[5][N]; int n; int f() { int cnt=0; for(int i=2;i<=n;i++) { if(a[i]!=a[i-1]+1) cnt++; } return (cnt+2)/3; } bool check() { for(int i=2;i<=n;i++) { if(a[i]!=a[i-1]+1) return false; } return true; } bool dfs(int step,int dep)//当前移动步数,深度限制 { if(step+f()>dep) return false;//超限制 if(check()) return true; //枚举开始位置l、移动长度len、移动到的位置loc for(int l=1;l<=n;l++) { for(int len=1;len+l-1<=n;len++) { int r=l+len-1; for(int loc=r+1;loc<=n;loc++) { //为什么t要开二维呢?因为需要往下递归然后回溯 //不能覆盖之前深度的数组 memcpy(t[step],a,sizeof a); //转移数组,更新状态 int x,y;//x表示新数组中的位置,y表示原始数组中的位置 for(x=r+1,y=l;x<=loc;x++,y++) { a[y]=t[step][x]; } for(x=l;x<=r;x++,y++) { a[y]=t[step][x]; } if(dfs(step+1,dep)) return true; memcpy(a,t[step],sizeof a); } } } return false; } int main() { int T; cin>>T; while(T--) { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; int dep=0; while(!dfs(0,dep)&&dep<5) { dep++; } if(dep>=5) cout<<"5 or more"<<endl; else cout<<dep<<endl; } }
以上代码参考yxc老师的部分代码,yxcyyds!
(仅代表个人思考。如有错误,欢迎礼貌指正。如有疑问,欢迎友好交流。鄙人愚笨,敬请原谅。)
???? Bye~