序列
【问题描述】
给定一个 1~n 的排列 x,每次你可以将 x1~xi 翻转。你需要求出将序列变为
升序的最小操作次数。有多组数据。
【输入格式】
第一行一个整数 t 表示数据组数。
每组数据第一行一个整数 n,第二行 n 个整数 x1~xn。
【输出格式】
每组数据输出一行一个整数表示答案。
【样例输入输出】
sequence.in
1
8
8 6 1 3 2 4 5 7
sequence.out
7
【数据范围】
对于 100%的测试数据,t=5,n<=25。
对于测试点 1,2,n=5。
对于测试点 3,4,n=6。
对于测试点 5,6,n=7。
对于测试点 7,8,9,n=8。
对于测试点 10,n=9。
对于测试点 11,n=10。
对于测试点 i (12<=i<=25),n=i。
每次将 n 翻转到 x1 再翻转到 xn,可以得到一个不超过
2n-2 步的做法。由于步数不多,我们可以使用迭代加深搜索。
我们发现每次翻转只会改变一对相邻数对,因此对于一个状态求出相差>1 的相邻数对的数量,剩余步数一定大于这个值。加上这个剪枝就能通过本题。
时间复杂度 O(能过)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<map> 6 using namespace std; 7 int a[26],ans,n,zyys,T; 8 int find() 9 {int i; 10 for (i=n;i>=1;i--,zyys++) 11 if (a[i]!=i) return i; 12 } 13 int count() 14 {int i; 15 int cnt=0; 16 for (i=n-1;i>=1;i--,zyys++) 17 if (abs(a[i]-a[i+1])!=1) cnt++; 18 return cnt; 19 } 20 bool dfs(int x) 21 {int i,j,flag; 22 int aa[26]={0}; 23 //if (zyys>90000000) return 1; 24 if (x>ans) return 0; 25 if (x+count()>ans) return 0; 26 flag=1; 27 for (i=n;i>=1;i--,zyys++) 28 if (a[i]!=i) 29 {flag=0;break;} 30 int r=i; 31 if (flag) 32 { 33 ans=x; 34 return 1; 35 } 36 memcpy(aa,a,sizeof(aa)); 37 zyys+=n; 38 for (i=1;i<=r;i++) 39 {zyys++; 40 for (j=1;j<=i;j++,zyys++) 41 a[j]=aa[i-j+1]; 42 if (dfs(x+1)) return 1; 43 for (j=1;j<=i;j++,zyys++) 44 a[j]=aa[j]; 45 } 46 return 0; 47 } 48 int main() 49 {int i,pos; 50 cin>>T; 51 while (T--) 52 { 53 cin>>n; 54 ans=n*2-2; 55 zyys=0; 56 for (i=1;i<=n;i++) 57 {zyys++; 58 scanf("%d",&a[i]); 59 if (a[i]==n) pos=i; 60 } 61 for (i=1;i<=2*n-2;i++) 62 {ans=i; 63 if (dfs(0)) break; 64 } 65 cout<<ans<<endl; 66 } 67 }