【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\n",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\n",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\n",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\n",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; }