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;
}