【8001】输入密码

Time Limit: 3 second
Memory Limit: 2 MB

【问题描述】

    John是某部门的工作人员,为提升安全等级,该部门的密码键盘是特殊设计的,键盘上没有数字按键, 而只有六个按键:
Up,Down,Left,Right,Sw0,Sw1,定义录入区域的六个位置的编号,从左至右依次为1,2,3,4,5,6。下面列出每个键的作用:

   Sw0:按Sw0,光标位置不变,将光标所在位置上的数字与录入区的1号位置上的数字交换。如果光标已经处在录入区
的1号位置,则按Sw0键之后,录入区的数字不变;
  Sw1:按Sw1,光标位置不变,将光标所在位置上的数字与录入区的6号位置上的数字交换。如果光标已经处在录入区
的6号位置,则按Sw1键之后,录入区的数字不变;
  Up:按Up,光标位置不变,将光标所在位置上的数字加1(除非该数字是9)。例如,如果光标所在位置的数字为2,按Up之后,
该处的数字变为3;如果该处数字为9,则按Up之后,数字不变,光标位置也不变;
  Down:按Down,光标位置不变,将光标所在位置上的数字减1(除非该数字是0),如果该处数字为0,则按Down之后,数字
不变,光标位置也不变;
  Left:按Left,光标左移一个位置,如果光标已经停留在录入区的1号位置(左起第一个位置)上,则光标不动;
  Right:按Right,光标右移一个位置,如果光标已经停留在录入区的6号位置(左起第六个位置)上,则光标不动。

  当然,为了使这样的键盘能够发挥作用,在每次开始要录入密码之前,录入区总是会随机出现一串长度为6的初始密码,
光标首先停留在1号位置上,当你巧妙地操作使用上述6个特殊按键之后,可以得到需要的目标密码,这时光标允许停留在任何
一个位置。
  现在,John有一个6位的数字密码,请编写一个程序,求出录入一个指定的密码需要的最少的操作击键次数。 

【输入】

输入数据为一行, 两个六位的数字, 之间用空格隔开。
第一个数据是指录入区上的初始数字(6位), 后加空格, 第二个为要输入的密码值(6为数字)。

【输出】

输出一行, 最少的按键次数

【输入样例】

123456 654321

【输出样例】

11


【题解】

已知起点求到终点的最少步骤数,广搜问题。这题不能用string类来做,虽然string类很方便,但是经过这一次,我彻底感受到了,string类在运行时有多么缓慢。。。将输入的两个数字先以string类的形式输入,然后把两个string类转换成int数组。用int数组来操作,会快很多。

然后是判重,用bool型数组bo[1000000][6]来判重,第一维是6位数字,第二维是光标的位置。然后在做广搜的时候用下循环队列,不然会超内存。

具体的看代码注释。

【代码】

#include <cstdio>
#include <string>
#include <stdlib.h>
#include <iostream>

using namespace std;

const int qq = 4500000;
const int qqq = 4500000+20; //这两个常量是用于循环队列 这个变量可以变成250W,那样占内存会更小。

int a,b,goal;
string ts1,ts2;
bool bo[1000000][6];
int dl1[qqq][6];
short dl2[qqq],dl3[qqq];
int s1[6],s2[6];


int get_num(int ss[6]) //将int数组转换成单个整形变量
{
    int l1 = 6,t = 0;
    int i = 0;
    while (i <= l1-1)
        {
            t = t*10 + ss[i];
            i++;
        }
    return t;
}

void input_data()
{
    cin >> ts1; //把string类转换成int数组
    cin >> ts2;
    for (int i = 0; i<= 5;i++)
        s1[i] = ts1[i] - '0';
    for (int i = 0;i <= 5;i++)
        s2[i] = ts2[i] - '0';
    goal = get_num(s2); //获取目标数字
    for (int i = 100000;i <= 999999;i++) //一开始把判重数组初始化
        for (int j = 0;j <= 5;j++)
            bo[i][j] = false;
}

void change(int & a,int & b) //交换数字a和b
{
    int t = a;
    a = b;
    b = t;
}

void get_ans() //开始广搜
{
    int head = 0,tail = 1;
    for (int i = 0;i <= 5;i++)
        dl1[1][i] = s1[i];
    dl2[1] = 0; //步数
    dl3[1] = 0; //光标位置
    int t = get_num(s1);
    bo[t][0] = true;
    while (head != tail) //循环队列不能写成 head <= tail
        {
            head++;
            int temp[6];
            for (int i = 0;i <= 5;i++) //取出队列的头结点
                temp[i] = dl1[head][i];
            int step = dl2[head],p = dl3[head];
            head = head % qq; //循环队列
            int num0 = get_num(temp); //将这个int数组转换成一个整形
            //和第0个位置上的数字交换
            if (p != 0) //如果光标不在第一位 就和第一位交换位置
                {
                    change(temp[p],temp[0]);
                    int num1 = get_num(temp); //再转换成数字
                    if (!bo[num1][p]) //判重
                        {
                            if(num1 == goal) //如果获得了目标就输出
                                {
                                    printf("%d
",step+1);
                                    exit(0);

                                }
                            bo[num1][p] = true; //判重
                            tail++; //把这个元素存入队列中  连同光标位置也要存
                            tail = tail %qq;
                            for (int i = 0;i <= 5;i++)
                                dl1[tail][i] = temp[i];
                            dl2[tail] = step+1; //同时还要存入步骤数。
                            dl3[tail] = p;
                        }
                    change(temp[0],temp[p]); //再把这个元素恢复原状
                }

            //和第5个位置上的数字交换
            if (p != 5) //下列步骤与 交换第0个位置的相同。参考上面的注释
                {
                    change(temp[p],temp[5]);
                    int numx = get_num(temp);
                    if (!bo[numx][p])
                        {
                            if(numx == goal)
                                {
                                    printf("%d
",step+1);
                                    exit(0);
                                }
                            bo[numx][p] = true;
                            tail++;
                            tail = tail % qq;
                            for (int i = 0;i <= 5;i++)
                                dl1[tail][i] = temp[i];
                            dl2[tail] = step+1;
                            dl3[tail] = p;
                        }
                    change(temp[5],temp[p]);
                }


            //光标所在位置上的数字+1;
            int temp1[6]; //如果光标位置上的数字是8或9,之后恢复原状会有点麻烦,所以直接对temp1而不是原数字进行操作。
            for (int i = 0;i <= 5;i++)
                temp1[i] = temp[i];
            if (temp1[p] != 9) //如果光标的数字不是9 那么就把这个数字递增
                {
                    temp1[p] += 1;
                    int num2 = get_num(temp1); //转成单个整形
                    if (!bo[num2][p]) //判重
                        {
                            if(num2 == goal) //如果等于目标,则输出.
                                {
                                    printf("%d
",step+1);
                                    exit(0);
                                }
                            bo[num2][p] = true; //判重
                            tail++; //把光标和数字和步骤数的信息存入队列
                            tail = tail %qq;
                            for (int i = 0;i <= 5;i++)
                                dl1[tail][i] = temp1[i];
                            dl2[tail] = step+1;
                            dl3[tail] = p;
                        }
                }



            //光标所在位置上的数字-1; 和+1的情形类似,参照上面注释
            for (int i = 0;i <= 5;i++)
                temp1[i] = temp[i];
            if (temp1[p] != 0)
                {
                    temp1[p] -= 1;
                    int num3 = get_num(temp1);
                    if (!bo[num3][p])
                        {
                            if (num3 == goal)
                                {
                                    printf("%d
",step+1);
                                    exit(0);
                                }
                            bo[num3][p] = true;
                            tail++;
                            tail = tail %qq;
                            for (int i = 0;i <= 5;i++)
                                dl1[tail][i] = temp1[i];
                            dl2[tail] = step+1;
                            dl3[tail] = p;
                        }
                }

            //光标左移一个位置; //因为有光标在0,1位置的情况,左移后都是1 不好还原 所以还是对pp光标进行操作
            int pp = p;
            if (pp != 0) //如果不是最左边 就操作(如果是最左边 操作这一步是没有意义的)
                {
                    pp--; //光标左移
                    if (!bo[num0][pp]) //判重
                        {
                            bo[num0][pp] = true;
                            tail++; //这次不用判断数字是否是目标,因为移动光标数字不会变的。
                            tail = tail % qq; //把数字和光标信息加入队列.
                            for (int i = 0;i <= 5;i++)
                                dl1[tail][i] = temp[i];
                            dl2[tail] = step +1;
                            dl3[tail] = pp;
                        }
                }

            //光标右移一个位置
            pp = p;  //和光标左移的情况相同 注释参考上面。
            if (pp != 5)
                {
                    pp++;
                    if (!bo[num0][pp])
                        {
                            bo[num0][pp] = true;
                            tail++;
                            tail = tail %qq;
                            for (int i = 0;i <= 5;i++)
                                dl1[tail][i] = temp[i];
                            dl2[tail] = step + 1;
                            dl3[tail] = pp;
                        }
                }
        }
}

int main()
{
    //freopen("F:\rush.txt","r",stdin);
    input_data();
    get_ans();
    return 0;
}


原文地址:https://www.cnblogs.com/AWCXV/p/7632433.html