NOI的题目,历经千辛万苦终于AC了……完全不在状态,今天下午写了一个下午写出来N多bug……各种悲剧……终于过了……写篇日志留念一下……上题目先- -
聪明的打字员
| **Time Limit:** 1000MS | **Memory Limit:** 65536K | |
| **Total Submissions:** 3232 | **Accepted:** 662 |
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
题目的意思其实很简单- -而且是中文的- - 这道题目其实是很久以前看英文吐血然后就找中文题做,一开始以为这道题目比较简单,是个软柿子,拿过来捏的,然后放了很久都没做出来……前天发现有一道题目没AC的,本着不留Faild的精神,决定A掉 一开始拿到题目,想着算上光标位置的话,总共只有6000000种状态,然后就想直接BFS……然后果断TLE了……看了Discuss里面各种说法,A啊,优化啊啥的 试着优化了一下感觉搞不定。 于是决定趁机学一下A……然后完全不知道咋下手…… 突然发现了一个好的优化办法,把数字增减跟移动分开考虑,先算出把当前排列变成所有其他排列需要的步数,然后再算出把那个状态变到需要的状态的加减的步数 写了个累死累活,终于写出来了- -由于移动也需要步数,一开始想的是把能够移动到的最右边的那个位置记下来,这样子有6!66个情况,能够接受 后来发现有个bug,999999到999998这种情况就没法过了……大悲剧…… 后来想到把能够改的每一位都做一个标记,然后具体写的时候由于是在刚才那个基础上面改,所以就搞了很别扭的位运算- -,把二进制高6位用于记录移动过程中有没有经过某位,然后低24位当10进制记录状态……混合进制……最后终于艰难的AC了…… 途中中bug无数,没想到一个80多行的程序能有这么多bug…… 其中由于位运算优先级导致的bug和重写一大堆 由于思路不清晰导致的数字写错bug一大堆 手误bug一大堆 思路错误小bug一大堆 算法错误大bug一大堆…… 好TM悲剧的一道题目…… 不过也算是AC了…… 另外左移操作是不能少的,比如下面这个数据 000159 000591 贴上代码……
#include <stdio.h>
#include <queue>
#include <set>
#include <map>
#include <stdlib.h>
using namespace std;
unsigned int mask[] = {100000, 10000, 1000, 100, 10, 1, 1000000, 10000000};
unsigned int couldMask[] = {0x80000000, 0x40000000, 0x20000000, 0x10000000, 0x8000000, 0x4000000};
map<unsigned int, unsigned int> Status;//值表示花费的步数,不存在表示未搜索到
queue<unsigned int> bfs_queue;
unsigned int ans, start, goal;
inline int Getpos(unsigned int status, unsigned int pos)
{
return ((status & 0xffffff) / mask[pos]) % 10;
}
unsigned int CheckAns(unsigned int status)
{
unsigned int total;
total = Status[status];
for(unsigned int i=0; i<6; ++i)
if(status & couldMask[i]){
total += abs(Getpos(status, i)- Getpos(goal, i));
}else if(Getpos(status, i)!= Getpos(goal, i))
return false;
if(total < ans)
ans = total;
}
unsigned int swap(unsigned int status, unsigned int p1, unsigned int p2)
{
return status + Getpos(status, p1)*(mask[p2]-mask[p1])
+ Getpos(status, p2)*(mask[p1]-mask[p2]);
}
void Add(unsigned int k, unsigned int step)
{
if(Status.find(k) == Status.end()){
Status[k] = step;
bfs_queue.push(k);
CheckAns(k);
}
}
int main()
{
unsigned int t;
scanf("%d%d", &start, &goal);
Status[start] = 0;
start |= couldMask[0];
bfs_queue.push(start);
ans = 100000000;
CheckAns(start);
while(!bfs_queue.empty()){
unsigned int now = bfs_queue.front();
bfs_queue.pop();
unsigned int step = Status[now]+1;
if(step > ans)
break;
if(Getpos(now, 6) > 0){
t = now - mask[6];
Add(t, step);
t = swap(now, 0, Getpos(now, 6));
t |= couldMask[0];
Add(t, step);
}
if(Getpos(now, 6) < 5){
t = now + mask[6];
t |= couldMask[Getpos(t, 6)];
Add(t, step);
t = swap(now, 5, Getpos(now, 6));
t |= couldMask[5];
Add(t, step);
}
}
printf("%d\n", ans);
}