POJ1184
聪明的打字员
Time Limit: 1000MS | Memory Limit: 65536K | |
---|---|---|
Total Submissions: 8480 | Accepted: 1842 |
Description
阿兰是某机密部门的打字员,她现在接到一个任务:需要在一天之内输入几百个长度固定为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号位置上。当巧妙地使用上述六个特殊键之后,可以得到目标密码,这时光标允许停在任何一个位置。
现在,阿兰需要你的帮助,编写一个程序,求出录入一个密码需要的最少的击键次数。
Input
仅一行,含有两个长度为6的数,前者为初始密码,后者为目标密码,两个密码之间用一个空格隔开。
Output
仅一行,含有一个正整数,为最少需要的击键次数。
Sample Input
123456 654321
Sample Output
11
Source
- 自己写的代码挂了, 参考别人的代码剪枝剪枝
- 具体的剪枝方式是剪枝了位置[1,4](从0开始)的左移和右移操作, 这四个位置的数字的改变;
- 当光标位置的数字和目标状态相同时, 需要通过光标的左移和右移去改变其他位置的数字
- 换句话说, 当光标位置的数字和目标状态不同时, 需要通过其他的操作改变该位置的数字, 因为迟早要改变, 不如现在就变.
- 详细的解释在代码中
- 有必要多去了解一下双端bfs, 状圧和剪枝的一些方式.
#pragma warning(disable:4996)
#include<cstdio>
#include <iostream>
#include <map>
#include <string>
#include <queue>
using namespace std;
struct Node {
string s;//6位数字, 第七位存储当前光标所在的位置[0,5]
int step;//达到的最小的步数
Node(string s_ = "", int step_ = 0) :s(s_), step(step_) {}
~Node() {}
};
//起点和终点
Node beg, en;
//一个hash,判断该状态是否出现过
map<string, int> mp;
queue<Node> q;
int bfs() {
Node tt;
int i;
while (!q.empty()) {
q.pop();
}
q.push(beg);
mp[beg.s] = 1;
while (!q.empty()) {
Node top = q.front();
q.pop();
string ss;
ss = top.s;
for (i = 0; i < 6; i++) {
if (ss[i] != en.s[i]) {
break;
}
}
if (i == 6) {
return top.step;
}
//Swap0:
ss = top.s;
swap(ss[0], ss[ss[6] - '0']);
if (!mp.count(ss)) {
tt.s = ss;
tt.step = top.step + 1;
q.push(tt);
mp[ss] = 1;
}
//Swap1:
ss = top.s;
swap(ss[5], ss[ss[6] - '0']);
if (!mp.count(ss)) {
tt.s = ss;
tt.step = top.step + 1;
q.push(tt); mp[ss] = 1;
}
//Up:
ss = top.s;
if (ss[ss[6] - '0'] != '9' && ss[ss[6] - '0'] != en.s[ss[6] - '0']) {
ss[ss[6] - '0'] += 1;
}
if (!mp.count(ss)) {
tt.s = ss;
tt.step = top.step + 1;
q.push(tt);
mp[ss] = 1;
}
//Down
ss = top.s;
if (ss[ss[6] - '0'] != '0' && ss[ss[6] - '0'] != en.s[ss[6] - '0']) {
ss[ss[6] - '0'] -= 1;
}
if (!mp.count(ss)) {
tt.s = ss; tt.step = top.step + 1;
q.push(tt); mp[ss] = 1;
}
//shift left
//左移的操作[1,4]的位置区间里面:
//如果[1,4]的区间里面光标所在的位置和目标状态不同, 进行左移和右移操作没有意义
//这里的证明?
//所有仅仅当相同的时候移动光标
ss = top.s;
if (ss[6] != '0') {
if (ss[6] != '5') {//光标所在的位置为1,2,3,,当且仅当相同的时候移动光标
if (ss[ss[6] - '0'] == en.s[ss[6] - '0']) {
ss[6] -= 1;
}
}
else {//光标所在的位置为5
ss[6] -= 1;
}
}
if (!mp.count(ss)) {
tt.s = ss; tt.step = top.step + 1;
q.push(tt); mp[ss] = 1;
}
//shift right
ss = top.s;
if (ss[6] - '0' != 5) {
if (ss[6] != '0') {
if (ss[ss[6] - '0'] == en.s[ss[6] - '0']) ss[6] += 1;
}
else {
ss[6] += 1;
}
}
if (!mp.count(ss)) {
tt.s = ss;
tt.step = top.step + 1;
q.push(tt);
mp[ss] = 1;
}
}
}
int main() {
while (cin >> beg.s >> en.s) {
mp.clear();
beg.s += '0';
beg.step = 0;
cout << bfs() << endl;
}
return 0;
}