Description:
给n的某种排列和k,问最少次操作可以将n的这个全排列变成1~n。
每次操作可以选择连续k个数,然后将它们翻转。
Input
第一行输入一个数T,表示测试数据个数
对于每组数据,第一行输入两个数n,k,然后第二行输入n个数,为1~n的某种排列。
数据范围:(1<=k<=n<=8)
Output
对于每组数据输出一个数,表示最少的操作次数,如果无法完成,输出-1.
Sample Input
3
3 3
1 2 3
3 3
3 2 1
5 4
3 2 4 5 1
Sample Output
0
1
-1
思路分析:1.最少的步骤数,很容易想到BFS,
2.主要是 状态如何存储,最多有8!=40320个排列,可以用康托展开式来存储,n!个排列对应数0-n!-1,就很方便记忆状态了
注意:1.queue用之前要清空
#include<iostream> #include<cstdio> #include<queue> #include<memory.h> using namespace std; int n,k; struct node { int arr[9]; int step; }; bool flag[100000]; queue<node>q; int fun(int x)//求阶乘 { if(x==1||x==0) return 1; else { int sum=1; for(int i=x;i>=1;i--) sum*=i; return sum; } } int cantor(node x)//求康托相对应的数 { int sum=0; /*for(int i=1;i<=n;i++) printf("%d**",x.arr[i]);*/ for(int i=1;i<=n;i++) { int temp=0; for(int j=i+1;j<=n;j++) if(x.arr[j]<x.arr[i]) temp++; sum+=temp*fun(n-i); } //printf("sum=%d\n",sum); return sum; } void inline swap(int &x,int &y) { int temp=x; x=y; y=temp; } node shift(node x,int i) { for(int j=1;j<=k/2;j++) swap(x.arr[i+j-1],x.arr[i+k-1-j+1]); return x; } int main() { int T; node a; scanf("%d",&T); while(T--) { memset(flag,0,sizeof(flag)); int ans=0; scanf("%d %d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&a.arr[i]); a.step=0; while(!q.empty())//忘了清空队列 q.pop(); q.push(a); /*for(int i=1;i<=n;i++) printf("%d ",a.arr[i]);*/ int x=cantor(a); //printf("x=%d\n",x); flag[x]=1; if(x==0) { printf("0\n"); continue; } int ok=0; while(!q.empty()) { node temp=q.front(); q.pop(); for(int i=1;i<=n-k+1;i++)//弄成i-k+1 { node t=shift(temp,i); t.step=temp.step+1; x=cantor(t); if(x==0) { ok=1; ans=t.step; break; } if(flag[x]==1)//出现过的状态 标记一下,避免再次遍历 continue; else { flag[x]=1; q.push(t); } } if(ok==1) break; } if(ok==0) ans=-1; printf("%d\n",ans); } return 0; }