蓝桥杯_ 历届试题 青蛙跳杯子 、 九宫重排

本文介绍了如何使用BFS(宽度优先搜索)算法解决两类问题:一是X星球青蛙跳跃游戏,二是九宫格数字拼图。详细解析了算法实现过程及代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述
  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


  资源约定:
  峰值内存消耗(含虚拟机) < 256M
  CPU消耗 < 1000ms




  请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。


  所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
  不要使用package语句。不要使用jdk1.7及以上版本的特性。
  主类的名字必须是:Main,否则按无效代码处理。


  ----------------------------


  笨笨有话说:
  我梦见自己是一棵大树,
  青蛙跳跃,
  我就发出新的枝条,
  春风拂动那第 5 层的新枝,
  哦,我已是枝繁叶茂。


    这个问题如果知道思路的话,并不是很难,但是想到这个思路就真的非常困难,所以把重点放在了如何根据这个题想到的使用BFS来做。

    首先我们知道只有一个'*'在这个字符串中,那么也就是两边的青蛙可以跳到这个空位置上来的只有六个,那么在这个跳完了之后,在这种形态之下,我们紧接着往后进行,那么我们就会利用到之前的状态,如果利用了之前的状态,我们就会发现,别的方法大多都是从这一条路一直走到天黑,如果这条路错了的话,才会回头,只有BFS会记录下之前的每一个状态,从这一个状态下,添加新的状态,所以还是BFS解决这个问题会好一些。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <map>
#include <queue>
#include <cmath>
#include <math.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int MAXN = 20;
string str,ans;
int dir[6] = {-1,-2,-3,1,2,3};
struct Node                          //结构体表示跳每一次的状态
{
    string s;                         //这种状态下青蛙的状态
    int step;                         //到达这种状态下,所需要的步数
    int pos;                          //*所在的位置
    Node(string S,int Step,int Pos)
    {
        s =S;step = Step;pos = Pos;
    }
    Node()
    {
    }
};
queue<Node> q;                   //BFS队列
map<string,int> mapp;            //我们为了防止产生重复,所以每一种状态都只能出现一次,map[string] ==1表示之前已经跳到过这种状态
void BFS()
{
    int len = str.size();     
    int pos = str.find('*');   
    Node p = Node(str,0,pos);      //构造函数生成结构体变量
    q.push(p);                     //放到队列中    
    mapp[str] = 1;                 //将这种状态保存为已经存在,到这里上面表示的是将最开始的状态放到队列中,然后再这个最开始的基础之上进行计算
    while(!q.empty())
    {
        Node temp;
        p = q.front();            //取出开始放入的一个元素
        q.pop();
        if(p.s == ans)            //找到答案,就将步数输出
        {
            cout<<p.step<<endl;
            return ;
        }
        for(int i =0 ;i < 6;i ++)  //只能朝六个方格行进,这里遍历六种情况
        {
            temp.pos = p.pos + dir[i];      //新的状态下*的位置
            if(temp.pos < 0 || temp.pos >= len)   //新位置不符合状态,直接删除掉
                continue;
            string str_temp = p.s;             
            swap(str_temp[temp.pos],str_temp[p.pos]);
            temp.s = str_temp;             //新状态下的新的字符串
            temp.step = p.step +1;         //步数要在原来基础之上加1,因为在原来基础之上跳了一步
            if(!mapp[temp.s])              //当map中不存在这种状态时
            {
                q.push(temp);              //放入队列
                mapp[temp.s] = 1;
            }
        }
    }
}

void Init()
{
    cin>>str>>ans;
}
void solve()
{
    BFS();
}
int main()
{
    Init();
    solve();
}
问题描述
  如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。

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

问题和上面的类似,只不过如果这里使用map中字符串相互匹配的话,很容易就会超时,所以将map中的字符串匹配转换为数字的匹配,这里面1234.5678转换为数字就是:123456784  最后一位表示小数点所在的位置,这样就乐意解决了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
#include <map>
#include <queue>
#include <algorithm>
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
const int MAXN = 15;
int dir[9][4] = {{1,3},{0,2,4},{1,5},{0,4,6},{1,3,5,7},{2,4,8},{3,7},{6,4,8},{5,7}};
int cnt[9] = {2,3,2,3,4,3,2,3,2};
string ans;
struct state
{
    string str;
    int pos;
    int step;
    state(string str,int pos,int step):str(str),pos(pos),step(step){}
    state(){}
};
map<int,int> m;
queue<state> que;
int get_num(string a)
{
    int num = 0,pos;
    for(int i = 0;i < 9;i ++)
    {
        if(a[i] == '.')
            pos = i;
        else
            num = num*10 + (a[i] - '0');
    }
    num = num*10 + pos;
    return num;
}
int bfs()
{
    while(!que.empty())
    {
        state temp = que.front();
        que.pop();
        if(temp.str == ans)
            return temp.step;
        for(int i = 0;i < cnt[temp.pos];i ++)
        {
            string s = temp.str;
            swap(s[temp.pos],s[dir[temp.pos][i]]);
            if(m[get_num(s)] == 0)
            {
                m[get_num(s)] ++;
                que.push(state(s,dir[temp.pos][i],temp.step+1));
            }
        }
    }
    return -1;
}
int main()
{
    ios::sync_with_stdio(false);
    state first;
    cin>>first.str>>ans;
    for(int i = 0;i < 9;i ++)
        if(first.str[i] == '.')
        {
            first.pos = i;
            break;
        }
    first.step = 0;
    m[get_num(first.str)] = 1;
    que.push(first);
    cout<<bfs()<<endl;
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值