poj 1184 聪明的打字员 (操作分离缩小状态+bfs)

聪明的打字员
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 5839   Accepted: 1190

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
 
 

题目大意:给2个数字串,给6种操作,要求最少的步数将第一串变成第二串。6种操作分别是:

1:swap0:光标位置不变,将光标位置数字与第一个数字交换;

2:swap1:光标位置不变,将光标位置数字与第六个数字交换;

3:left:如果光标不在1位置,光标左移一位;

4:right:如果光标不在6位置,光标右移一位;

5:up:光标位置不变,如果光标位置数字小于9,光标位置数字加1,否则不变;

6:down:光标位置不变,如果光标位置数字大于0,光标位置数字减一,否则不变;

思路:6种操作可以分成2类:1-4操作是第一类,能改变初始状态每个数的位置,5-6操作能改变光标所访问到的位置的数。所以我们将改变数值的操作和改变光标位置的操作分离开来,对改变光标位置的操作进行bfs,最后与结果比较,光标经过的位置可以改变大小,每一位的改变数值的操作次数为该状态与目标状态绝对值之和。

oh-sorry ! 忘记说还要考虑光标的访问状态了 光标访问状态有10种 具体见代码 只有被光变访问了的位置的数才能用第二种操作来改变大小

ps:这题网上很多题解 都说左移是不必要的 但是左移是必须的 不信可以试下下面的数据

000159 000519 ans:8 700638 815339 ans:14

感想:开始拿到这道题目 天真的用朴素的单搜做了 华丽丽的TLE 可怜 只能怪自己没有预估状态 这题直接处理状态数太多 有6*1000000种 显然不管是单搜还是双搜都是处理不了的

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map>
using namespace std;

int ans,cnt;
int tpos,tstep,tst;
int s1[6],s2[6],s22[6],st[6];
char in1[10],in2[10];
int start[10],end[10];
int vis[6][6][6][6][6][6][6][10];    // 判重数组   0~5-各位置所对应的值  6~光标所在位置 7~光标访问状态
int mouse[10][6]=                    // 光标的10种访问情况
{
    1,0,0,0,0,0,
    1,1,0,0,0,0,
    1,1,1,0,0,0,
    1,1,1,1,0,0,
    1,1,1,1,1,0,
    1,1,1,1,1,1,
    1,0,0,0,0,1,
    1,1,0,0,0,1,
    1,1,1,0,0,1,
    1,1,1,1,0,1
};
struct Node
{
    int s[6];
    int pos,step,state;
} cur,now;
int value[44000][8];     // 0~5 对应的s的值 6~state 7~step   将各种状态记录下来
queue<Node>q;

int getstate()           // 获取光标访问状态
{
    int i,j,flag;
    for(i=0;i<10;i++)
    {
        flag=1;
        for(j=0;j<6;j++)
        {
            if(mouse[i][j]!=st[j]) flag=0;
        }
        if(flag) return i;
    }
}
void bfs()
{
    int i,j,temp;
    while(!q.empty()) q.pop();
    memset(vis,0,sizeof(vis));
    cnt=0;
    for(i=0;i<6;i++)
    {
        cur.s[i]=i;
    }
    cur.pos=0;
    cur.step=0;
    cur.state=0;
    q.push(cur);
    vis[0][1][2][3][4][5][0][0]=1;          
    while(!q.empty())
    {
        now=q.front();
        tstep=now.step;
        tpos=now.pos;
        tst=now.state;
        memcpy(s1,mouse[tst],sizeof(s1));
        memcpy(s2,now.s,sizeof(s2));
        cnt++;
        for(i=0;i<6;i++)
        {
            value[cnt][i]=s2[i];
        }
        value[cnt][6]=tst;
        value[cnt][7]=tstep;
        if(tpos!=0)               // 左移
        {
            cur.pos=tpos-1;
            memcpy(st,s1,sizeof(s1));
            st[tpos-1]=1;
            cur.state=getstate();
            if(!vis[s2[0]][s2[1]][s2[2]][s2[3]][s2[4]][s2[5]][tpos-1][cur.state])
            {
                vis[s2[0]][s2[1]][s2[2]][s2[3]][s2[4]][s2[5]][tpos-1][cur.state]=1;
                memcpy(cur.s,s2,sizeof(s2));
                cur.step=tstep+1;
                q.push(cur);
            }
        }
        if(tpos!=5)               // 右移
        {
            cur.pos=tpos+1;
            memcpy(st,s1,sizeof(s1));
            st[tpos+1]=1;
            cur.state=getstate();
            if(!vis[s2[0]][s2[1]][s2[2]][s2[3]][s2[4]][s2[5]][tpos+1][cur.state])
            {
                vis[s2[0]][s2[1]][s2[2]][s2[3]][s2[4]][s2[5]][tpos+1][cur.state]=1;
                memcpy(cur.s,s2,sizeof(s2));
                cur.step=tstep+1;
                q.push(cur);
            }
        }
        if(tpos!=0)               // 与0号位置交换
        {
            memcpy(st,s1,sizeof(s1));
            memcpy(s22,s2,sizeof(s2));
            temp=s22[tpos];
            s22[tpos]=s22[0];
            s22[0]=temp;
            st[0]=1;
            cur.state=getstate();
            if(!vis[s22[0]][s22[1]][s22[2]][s22[3]][s22[4]][s22[5]][tpos][cur.state])
            {
                vis[s22[0]][s22[1]][s22[2]][s22[3]][s22[4]][s22[5]][tpos][cur.state]=1;
                cur.step=tstep+1;
                cur.pos=tpos;
                memcpy(cur.s,s22,sizeof(s22));
                q.push(cur);
            }
        }
        if(tpos!=5)              // 与5号位置交换
        {
            memcpy(st,s1,sizeof(s1));
            memcpy(s22,s2,sizeof(s2));
            temp=s22[tpos];
            s22[tpos]=s22[5];
            s22[5]=temp;
            st[5]=1;
            cur.state=getstate();
            if(!vis[s22[0]][s22[1]][s22[2]][s22[3]][s22[4]][s22[5]][tpos][cur.state])
            {
                vis[s22[0]][s22[1]][s22[2]][s22[3]][s22[4]][s22[5]][tpos][cur.state]=1;
                cur.step=tstep+1;
                cur.pos=tpos;
                memcpy(cur.s,s22,sizeof(s22));
                q.push(cur);
            }
        }
        q.pop();
    }
}
int main()
{
    int i,j,sum,fflag;
    bfs();                           // 一次bfs就ok了  能够存下6!前四种操作后的每个数字对应的位置及光标访问状态
    while(~scanf("%s%s",in1,in2))
    {
        for(i=0;i<6;i++)
        {
            start[i]=in1[i]-'0';
            end[i]=in2[i]-'0';
        }
        ans=100000000;
        for(i=1;i<=cnt;i++)                     // 每次枚举所有情况更新ans
        {
            sum=0;
            fflag=1;
            for(j=0;j<6;j++)
            {
                if(mouse[value[i][6]][j])       // 只有被光标访问了的位置才能改变大小
                {
                    sum+=fabs(end[j]-start[value[i][j]]);
                }
                else
                {
                    if(end[j]!=start[value[i][j]]) fflag=0;   // 如果没访问  且值不与最终状态相同 肯定不行
                }
            }
            if(fflag)
            {
                if(ans>sum+value[i][7]) ans=sum+value[i][7];  
            }
        }
        printf("%d\n",ans);
    }
}
/*
000159 000519     ans:8
700638 815339     ans:14
*/




你可能感兴趣的:(poj 1184 聪明的打字员 (操作分离缩小状态+bfs))