JZOJ_7.7C组第三题 页

题意

给出奇数个数,每次把它中间的数取出来放到最左边或最右边,求出最少取多少次可以让这些数是从小到大的。

思路

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

猜你喜欢

转载自www.cnblogs.com/HSZGB/p/9303000.html