题意
给出奇数个数,每次把它中间的数取出来放到最左边或最右边,求出最少取多少次可以让这些数是从小到大的。
思路
bfs+hash判重。每次左边右边操作一次,判断之前有没有出现过了,没有就入队。
代码
#include<cstdio>
#define MAXN 1000003
using namespace std;
int n,state[1000001][11],head,tail,bs[1000001],mid,a[1000001];
long long hs[MAXN];
bool ok;
long long locate(int a[])
{
long long b=0;
for (int i=1;i<=n;i++) b=(b<<3)+(b<<1)+a[i];
return b;
}
bool hash(int a[])
{
long long f=locate(a);int y=f%MAXN;
while(hs[y]&&hs[y]!=f) y=(y+1)%MAXN;
if(!hs[y]) return true;
return false;
}
void left()
{
int t=a[mid];
for(int i=mid;i>1;i--) a[i]=a[i-1];
a[1]=t;
}
void right()
{
int t=a[mid];
for(int i=mid;i<n;i++) a[i]=a[i+1];
a[n]=t;
}
bool check()
{
for (int i=1;i<n;i++) if (a[i]>a[i+1]) return 0;
return 1;
}
void bfs()
{
if (hash(state[1])) n=n;
head=0;tail=1;
do
{
head=head%1000001+1;//循环队列
for (int i=1;i<=n;i++) a[i]=state[head][i];//a用来记录操作的状态
left();//往左
if (hash(a))//如果之前没搜过就可以进队
{
tail=tail%1000001+1;
for(int i=1;i<=n;i++) state[tail][i]=a[i];
bs[tail]=bs[head]+1;//记录答案
}
if (check()) {ok=1;printf("%d",bs[tail]);return;}
for (int i=1;i<=n;i++) a[i]=state[head][i];
right();//往右
if (hash(a))
{
tail=tail%1000001+1;
for(int i=1;i<=n;i++) state[tail][i]=a[i];
bs[tail]=bs[head]+1;
}
if (check()) {ok=1;printf("%d",bs[tail]);return;}
}
while (head!=tail);
}
int main()
{
scanf("%d",&n);mid=(n+1)>>1;
for (int i=1;i<=n;i++) scanf("%d",&state[1][i]),state[1][i]-=159;
bfs();
if(!ok)printf("No Answer");
}