蓝桥杯——九宫重排、青蛙跳杯子

1.历届试题 九宫重排  

时间限制:1.0s   内存限制:256.0MB
      
问题描述
  如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。

  我们把第一个图的局面记为:12345678.
  把第二个图的局面记为:123.46758
  显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
  本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。
输入格式
  输入第一行包含九宫的初态,第二行包含九宫的终态。
输出格式
  输出最少的步数,如果不存在方案,则输出-1。
样例输入
12345678.
123.46758
样例输出
3
样例输入
13524678.
46758123.
样例输出
22
思考过程:

九宫格数字的移动,其实是由空格移动造成的,所以,站在空格的角度上,将空格当作运动的点,有四种运动方向:前后左右。以前迷宫问题, 就是起点走到终点,深度搜索(队列),最先到达终点时的步数,就是最短路径;九宫重排问题一样,用字符串表示九宫格的状态,遍历的过程中,当 到达的状态和目标状态一致时结束,但是怎么避免老是走重复的路呢(排重)?那就需要将过程中的状态全部记录下来,每走到新的状态,比较一下先前是否出现过,若出现过,就停止走这条路,即不让这一状态入队列即可。将先前的状态放入set容器可以很快速知道是否出现过。

第一次 TLE 运行超时了,60分;

第二次 TLE 运行超时了,80分, 将操作(1)、(2)交换位置,要986ms,说明没有在根源上减少时间。

第三次,100昏,将结构体增加'.'(空格)的坐标,省去每次找坐标的时间。

<c++代码>
 1 #include<stdio.h>
 2 #include<set>
 3 #include<queue>
 4 #include<string.h>
 5 #include<string>
 6 using namespace std;
 7 struct node
 8 {
 9     int step,pos_x,pos_y;
10     char str[15];
11 };
12 char str1[15],str2[15];//初始状态、目标状态
13 int D[4][2]= {-1,0,1,0,0,-1,0,1};
14 set<string> Set;//定义一个string类型的set容器,存放九宫格的状态
15 queue<node> que;//定义一个队列,存放行走过程中的状态,每一个node包含达到此状态的步数step、和当时的状态
16 int blank_pos(char *str) //空格的位置
17 {
18     int l=strlen(str);
19     for(int i=0; i<l; i++)
20     {
21         if(str[i]=='.')
22             return i;
23     }
24 }
25 int bfs()
26 {
27     char next_str[15];
28     node n,new_node;
29     int pos,pos_x,pos_y,next_x,next_y,next_pos;
30     while(!que.empty())
31     {
32         n=que.front();//队首出队
33         que.pop();
34         for(int i=0; i<4; i++) //空格往四个方向行走
35         {
36             strcpy(next_str,n.str);//将来next_str要在n.str的基础上改动,但是n.str一直不能动,因为是四个方向共用的
37             next_x=n.pos_x+D[i][0];
38             next_y=n.pos_y+D[i][1];
39             if(next_x>=0&&next_x<=2&&next_y>=0&&next_y<=2) //将要走的那一步符合要求
40             {
41                 //将接下来的状态表示出来(空格上的’0‘ 变为 (next_x,next_y)上的字符,(next_x,next_y)变成空格;查看set里是否有这个状态,若有就不走这一步,避免重复)
42                 pos=n.pos_x*3+n.pos_y;
43                 next_pos=next_x*3+next_y;//新空格位置
44                 next_str[next_pos]='.';
45                 next_str[pos]=n.str[next_pos];
46 //判重
47                 if(Set.find(next_str)==Set.end()) //表示没有找到重的
48                 {
49                     new_node.step=n.step+1;
50                     //(1)查看着个状态是不是目标状态
51                     if(!strcmp(next_str,str2)) //即得到了目标状态
52                     {
53                         return new_node.step;
54                     }
55                     //(2)将新的状态记录在队列和Set里
56                     strcpy(new_node.str,next_str);
57                     new_node.pos_x=next_x;
58                     new_node.pos_y=next_y;
59                     que.push(new_node);
60                     Set.insert(next_str);
61                 }
62             }
63         }
64     }
65     return -1;
66 }
67 int main()
68 {
69 //输入
70     node n;
71     int min,pos;
72     scanf("%s",str1);
73     scanf("%s",str2);//目标
74     if(!strcmp(str1,str2)) //即得到了目标状态
75     {
76         printf("%d",0);
77     }
78     else
79     {
80 //初始状态放入队列和set容器
81         pos=blank_pos(str1);//找'.'的位置
82         n.pos_x=pos/3;
83         n.pos_y=pos%3;
84         n.step=0;
85         strcpy(n.str,str1);
86         que.push(n);
87         Set.insert(n.str);
88         min=bfs();//广搜,找出最少步骤
89         printf("%d",min);
90     }
91     return 0;
92 }
View Code

2.历届试题 青蛙跳杯子  

时间限制:1.0s   内存限制:256.0MB
    
问题描述
  X星球的流行宠物是青蛙,一般有两种颜色:白色和黑色。
  X星球的居民喜欢把它们放在一排茶杯里,这样可以观察它们跳来跳去。
  如下图,有一排杯子,左边的一个是空着的,右边的杯子,每个里边有一只青蛙。


  *WWWBBB


  其中,W字母表示白色青蛙,B表示黑色青蛙,*表示空杯子。


  X星的青蛙很有些癖好,它们只做3个动作之一:
  1. 跳到相邻的空杯子里。
  2. 隔着1只其它的青蛙(随便什么颜色)跳到空杯子里。
  3. 隔着2只其它的青蛙(随便什么颜色)跳到空杯子里。


  对于上图的局面,只要1步,就可跳成下图局面:


  WWW*BBB


  本题的任务就是已知初始局面,询问至少需要几步,才能跳成另一个目标局面。


  输入为2行,2个串,表示初始局面和目标局面。
  输出要求为一个整数,表示至少需要多少步的青蛙跳。
样例输入
*WWBB
WWBB*
样例输出
2
样例输入
WWW*BBB
BBB*WWW
样例输出
10
数据规模和约定
  我们约定,输入的串的长度不超过15
 
 
思路:和九宫重排一模一样,在九宫重排(60分代码)的代码上稍作改动,

青蛙跳杯子,感觉和九宫重排很相似,虽然是青蛙在跳,但是可以看成空杯子在跳,有6种跳法:往左一个、往右一个,往左两个、往右两个,往左三个、往右三个。诶等一下!!会不会不只一个空杯子?、这样就不行了,不管了,就先当所有案例的都只有一个空杯子,两个的话,就2*6种跳法?后来提交代码后,发现只用考虑一个空杯子。

<c++代码>

 1 include<stdio.h>
 2 #include<set>
 3 #include<string>
 4 #include<string.h>
 5 #include<queue>
 6 using namespace std;
 7 struct node{
 8     int step;
 9     char str[20];
10 };
11 int D[6]={-1,1,-2,2,-3,3};
12 int len;//杯子个数
13 char str1[20],str2[20];//初始状态、目标状态
14 set<string> Set;//用Set存放跳的过程中的状态
15 queue<node> que;//node每一次跳跃之后的状态和已经跳跃的步数
16 int blank_pos(char *str) //空格的位置
17 {
18     for(int i=0; i<strlen(str); i++)
19     {
20         if(str[i]=='*')
21             return i;
22     }
23 }
24 int bfs()
25 {
26     char next_str[20];
27     node n,new_node;
28     int pos,next_pos;
29     while(!que.empty())
30     {
31         n=que.front();//队首出队
32         //printf("%s
",n.str);
33         que.pop();
34         //得出'*'的位置
35         pos=blank_pos(n.str);
36         for(int i=0; i<6; i++) //*有五种跳法
37         {
38             strcpy(next_str,n.str);//将来next_str要在n.str的基础上改动,但是n.str一直不能动,因为是四个方向共用的
39             next_pos=pos+D[i];
40             if(next_pos>=0&&next_pos<len) //将要走的那一步符合要求
41             {
42                 //将接下来的状态表示出来('*'变为 next_pos上的字符,next_pos上的字符变成'*';查看set里是否有这个状态,若有就不走这一步,避免重复)
43 
44                 next_str[next_pos]='*';
45                 next_str[pos]=n.str[next_pos];
46 //判重
47                 if(Set.find(next_str)==Set.end()) //表示没有找到重的
48                 {
49                     //将新的状态记录在队列和Set里
50                     strcpy(new_node.str,next_str);
51                     new_node.step=n.step+1;
52                     que.push(new_node);
53                     Set.insert(next_str);
54                     //查看着个状态是不是目标状态
55                     if(!strcmp(next_str,str2)) //即得到了目标状态
56                     {
57                         return new_node.step;
58                     }
59                 }
60             }
61         }
62     }
63     return -1;
64 }
65 int main(){
66     node n;
67     int min;
68 scanf("%s",str1);
69     scanf("%s",str2);//目标
70     len=strlen(str1);
71     if(!strcmp(str1,str2)) //即得到了目标状态
72     {
73         printf("%d",0);
74     }
75     else
76     {
77 //初始状态放入队列和set容器
78         n.step=0;
79         strcpy(n.str,str1);
80         que.push(n);
81         Set.insert(n.str);
82         min=bfs();//广搜,找出最少步骤
83         printf("%d",min);
84     }
85 return 0;
86 }
View Code
原文地址:https://www.cnblogs.com/li-yaoyao/p/10547359.html