洛谷 P1949 聪明的打字员 [NOI导刊2011提高(10)] (bfs+剪枝)

题目描述

阿兰是某机密部门的打字员,她现在接到一个任务:需要在一天之内输入几百个长度固定为6的密码。当然,她希望输入的过程中敲击键盘的总次数越少越好。

不幸的是,出于保密的需要,该部门用于输入密码的键盘是特殊设计的,键盘上没有数字键,而只有以下六个键:swap0,swap1,up,down,left,right。为了说明这6个键的作用,我们先定义录入区的6个位置的编号,从左至右依次为1,2,3,4,5,6。下面列出每个键的作用:

swap0:按swap0,光标位置不变,将光标所在的位置的数字与录入区的1号位置的数字(左起第一个数字)交换。如果光标已经处在录入区的1号位置,则按swap0键之后录入区的数字不变。

swap1:按swap1,光标位置不变,将光标所在位置的数字与录入区的6号位置的数字(左起第六个数字)交换。如果光标已经处在录入区的6号位置,则按swap1键之后录入区的数字不变。

up:按up,光标位置不变,讲光标所在位置的数字加1(除非该数字是9)。例如,如果光标所在位置的数字为2,按up之后,该处的数字变为3;如果光标所在位置的数字为9,按up之后,该处的数字不变,光标位置也不变;

down:按down,光标位置不变,讲光标所在位置的数字减1(除非该数字是0)。如果光标所在位置的数字为0,按down之后,该处的数字不变,光标位置也不变;

left:按left,光标左移一个位置,如果光标已在录入区的1号位置(左起第一个位置)上,则光标不动;

right:按right,光标右移一个位置,如果光标已在录入区的6号位置(左起第六个位置)上,则光标不动;

当然,为了使这样的键盘发挥作用,每次录入密码之前,录入区总会随机出现一个长度为6的初始密码,而且光标会固定出现在1号位置上。当巧妙的使用上述六个特殊键之后,可以得到目标密码,这时光标允许停留在任何一个位置。

现在,阿兰需要你的帮助,编写一个程序,求出录入一个密码需要的最少的击键次数。
输入输出格式

输入格式:

仅一行,含有两个长度为6的数,前者为初始密码,后者为目标密码,两个密码之间用一个空格隔开。

输出格式:

仅一行,含有一个正整数,为最少需要的击键次数。

算法

以密码栏整体为状态,bfs,类似与八数码?
剪枝:
上一步是加,这一步就不减,反之同理
上一步左移,这一步不右移,反之同理
总共六个位置,数字可能不会占满[1,9],所以广搜范围可以缩小
如 234567 就可以不搜0 1 8 9
如 127788 就可以不搜0 9,中间的不可以减啊““`因为数字是一个一个涨上去的,把路断了就上不去了。

代码

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
const int c=10;
bool vis[c][c][c][c][c][c][7];
int ans,mrn=999999,mxn=-1;
struct lock
{
    int a[7];
    int sum,cur,pre;

    lock(){sum=0;}
    void operator = (int x)
    {
        int bit[7]={0,100000,10000,1000,100,10,1};
        for(int i=1;i<=6;i++)
        {
            a[i]=x/bit[i]%10;
            mrn=min(mrn,a[i]);
            mxn=max(mxn,a[i]);
        }        
        cur=1;pre=-1;
    }
    bool operator == (lock x)
    {
        for(int i=1;i<=6;i++)
            if(a[i]!=x.a[i])
                return 0;
        return 1;
    }
    void s0(){swap(a[1],a[cur]);sum++;}
    void s1(){swap(a[6],a[cur]);sum++;}
    void up(){a[cur]++;sum++;}
    void dw(){a[cur]--;sum++;}
    void lt(){cur--;sum++;}
    void rt(){cur++;sum++;}
}s,t;
queue<lock> q;
bool check(lock e)
{
    if(t==e){ans=e.sum;return 1;}
    if(!vis[e.a[1]][e.a[2]][e.a[3]][e.a[4]][e.a[5]][e.a[6]][e.cur])
    {
        vis[e.a[1]][e.a[2]][e.a[3]][e.a[4]][e.a[5]][e.a[6]][e.cur]=1;
        q.push(e);
    }
    return 0;
}
void bfs()
{
    vis[s.a[1]][s.a[2]][s.a[3]][s.a[4]][s.a[5]][s.a[6]][s.cur]=1;
    q.push(s);
    while(!q.empty())
    {
        lock e;
        e=q.front();
        if(e.cur>1)
        {
            e.s0();e.pre=0;
            if(check(e))break;
            e=q.front();
            if(e.pre!=5)
            {
                e.lt();e.pre=4;
                if(check(e))break;
            }
        }   
        e=q.front();
        if(e.cur<6)
        {
            e.s1();e.pre=1;
            if(check(e))break;
            e=q.front();
            if(e.pre!=4)
            {
                e.rt();e.pre=5;
                if(check(e))break;
            }        
        }
        e=q.front();
        if(e.a[e.cur]<mxn&&e.pre!=3)
        {
            e.up();e.pre=2;if(check(e))break;
        }
        e=q.front();
        if(e.a[e.cur]>mrn&&e.pre!=2)
        {
            e.dw();e.pre=3;if(check(e))break;;  
        }
        q.pop();
    }
}

int main()
{
    int x,y;
    mem(vis,0);
    scanf("%d%d",&x,&y);
    if(x==y)printf("0");
    else {s=x;t=y;bfs();printf("%d",ans);}


return 0;
}

常数有点大,洛谷上有一个点过不去,开了个o2。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值