1.历届试题 九宫重排
时间限制:1.0s 内存限制:256.0MB
问题描述
如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。
我们把第一个图的局面记为:12345678.
把第二个图的局面记为:123.46758
显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。
我们把第一个图的局面记为:12345678.
把第二个图的局面记为:123.46758
显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。
输入格式
输入第一行包含九宫的初态,第二行包含九宫的终态。
输出格式
输出最少的步数,如果不存在方案,则输出-1。
样例输入
12345678.
123.46758
123.46758
样例输出
3
样例输入
13524678.
46758123.
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 }
2.历届试题 青蛙跳杯子
时间限制:1.0s 内存限制:256.0MB
问题描述
X星球的流行宠物是青蛙,一般有两种颜色:白色和黑色。
X星球的居民喜欢把它们放在一排茶杯里,这样可以观察它们跳来跳去。
如下图,有一排杯子,左边的一个是空着的,右边的杯子,每个里边有一只青蛙。
*WWWBBB
其中,W字母表示白色青蛙,B表示黑色青蛙,*表示空杯子。
X星的青蛙很有些癖好,它们只做3个动作之一:
1. 跳到相邻的空杯子里。
2. 隔着1只其它的青蛙(随便什么颜色)跳到空杯子里。
3. 隔着2只其它的青蛙(随便什么颜色)跳到空杯子里。
对于上图的局面,只要1步,就可跳成下图局面:
WWW*BBB
本题的任务就是已知初始局面,询问至少需要几步,才能跳成另一个目标局面。
输入为2行,2个串,表示初始局面和目标局面。
输出要求为一个整数,表示至少需要多少步的青蛙跳。
X星球的居民喜欢把它们放在一排茶杯里,这样可以观察它们跳来跳去。
如下图,有一排杯子,左边的一个是空着的,右边的杯子,每个里边有一只青蛙。
*WWWBBB
其中,W字母表示白色青蛙,B表示黑色青蛙,*表示空杯子。
X星的青蛙很有些癖好,它们只做3个动作之一:
1. 跳到相邻的空杯子里。
2. 隔着1只其它的青蛙(随便什么颜色)跳到空杯子里。
3. 隔着2只其它的青蛙(随便什么颜色)跳到空杯子里。
对于上图的局面,只要1步,就可跳成下图局面:
WWW*BBB
本题的任务就是已知初始局面,询问至少需要几步,才能跳成另一个目标局面。
输入为2行,2个串,表示初始局面和目标局面。
输出要求为一个整数,表示至少需要多少步的青蛙跳。
样例输入
*WWBB
WWBB*
WWBB*
样例输出
2
样例输入
WWW*BBB
BBB*WWW
BBB*WWW
样例输出
10
数据规模和约定
我们约定,输入的串的长度不超过15
思路:和九宫重排一模一样,在九宫重排(60分代码)的代码上稍作改动,
View Code
青蛙跳杯子,感觉和九宫重排很相似,虽然是青蛙在跳,但是可以看成空杯子在跳,有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 }