序列

【问题描述】
给定一个 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 }

 

posted @ 2017-11-06 14:34  Z-Y-Y-S  阅读(361)  评论(0编辑  收藏  举报