JZOJ_7.7C组第三题 页

题意

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

思路

bfs+hash判重。每次左边右边操作一次,判断之前有没有出现过了,没有就入队。

95分代码(这里hash好像打的不是很好)

#include<cstdio>
#define MAXN 300017
using namespace std;
int n,state[1000001][11],head,tail,hs[MAXN],father[1000001],ans;
int locate(int x)
{
    int b=0;
    for (int i=1;i<=n;i++)
        b=b+state[x][i]*state[x][i-1]*i;
    return b%MAXN;
}
bool hash(int x)
{
    int f=locate(x),i=0;
    while (i<MAXN&&hs[(i+f)%MAXN]>0&&hs[(i+f)%MAXN]!=f) i++;
    if (hs[(i+f)%MAXN]==0) {hs[(i+f)%MAXN]=f;return 1;}
    else return 0;
}
void left()//左边
{
    for (int i=n/2+1;i>=2;i--)
        state[tail][i]=state[head][i-1];
    state[tail][1]=state[head][n/2+1];
    for (int i=n/2+2;i<=n;i++)
        state[tail][i]=state[head][i];
}
void right()//右边
{
    for (int i=n/2+1;i<n;i++)
        state[tail][i]=state[head][i+1];
    state[tail][n]=state[head][n/2+1];
    for (int i=1;i<n/2+1;i++)
        state[tail][i]=state[head][i];
}
int check(int x)
{
    for (int i=2;i<=n;i++)
        if (state[x][i]<state[x][i-1]) return 0;//判断从小到大
    return 1;
}
void Ans(int x)//回溯求答案
{
    while (father[x]!=0) 
    {
        ans++;
        x=father[x];
    }
}
void bfs()
{
    if (hash(1)) n=n;//先把初始状态扔进hash表里
    head=0;tail=1;
    do
    {
        head++;
        tail++;
        left();
        if (!hash(tail)) tail--;//如果不可以左边我们就不入队
        else father[tail]=head;//记录father求答案
        if (check(tail)) {Ans(tail);return;}//如果已经从小到大了就退出
        tail++;
        right();
        if (!hash(tail)) tail--;
        else father[tail]=head;
        if (check(tail)) {Ans(tail);return;}
    }
    while (head<tail);
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        scanf("%d",&state[1][i]);
    bfs();
    ans!=0?printf("%d",ans):printf("No Answer");
}

AC了!

#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");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值