魔板(搜索求最小步数模型题)

魔板(搜索之 “最小步数”)

题目来源:AcWing(特别良心的一个网站,“学算法,就上AcWing!”

题目地址:魔板

整体思路:

1、从初始状态要想到该状态的话,一共为我们提供了三种操作,A,B,C,那我们想到这个状态的话,我们就这三种操作都会使用

2、但是我们又不知道具体的我们该如何去操作,所以我们就枚举每一种操作的可能,这就是bfs的搜索

3、因为我们有三种操作,所以我们就将三种操作任意组合起来,然后每次都搜索一下就好了

4、也就是说我们通过搜索的方式实现我们本不知道如何组织的操作(也就是我们不知道怎么找最优解,就把所有情况都枚举一遍)

5、然后得到这么一个结果,而我们利用宽度优先搜索的方式第一次搜到的一定是最小的操作次数了

6、然后这个题目特别的让我们要按照字典序最小输出

7、y总说其实这种用字典序最小输出一般来说不会影响原题最开始的算法,只不过是说为了出题者更好的判题进行服务

8、所以我们只需要稍微做一些考虑就好了,不用想的那么复杂,原本的算法是不会被改变的

9、所以总结起来就是,这种题目,由于确实无法准确的知道最优解,那么我们就枚举所有的方式,知道找到最优解

10、特别的,这题目的状态需要特殊处理的表示,也算是这种题目的特色以及难点吧,也是需要我们这些新手来特殊学习的!

下面是代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<unordered_map>
#include<queue>

#define l first
#define r second
using namespace std;

const int N=5;

char a[N][N];//这只是一个中间过渡的变量

//第一个参数代表我们当前是哪一个字符串,后面第一个参数代表我们当前是从哪一步(A/B/C)过来的,以及我们是从哪个字符串过来的
unordered_map<string,pair<char,string>>pre;//第一个参数代表的是宿主,后面的是代表数组的一些性质

unordered_map<string,int>dist;



void set(string state)//把我们的字符串先转换为数组,以便我们进行题中描述的操作
{
    for(int i=0;i<4;i++)a[1][i]=state[i];//第一行
    
    for(int i=7,j=0;j<4;i--,j++)a[2][j]=state[i];//第二行
    
    //0~3顺序赋值
    //4~7逆序赋值
}

string get()//再将操作完的数组换成字符串
{
    string s;
    
    for(int i=0;i<=3;i++)s+=a[1][i];
    
    for(int i=3;i>=0;i--)s+=a[2][i];
    
    //第一行顺着给它,第二行,逆着给它
    
    return s;
    
}

string change1(string state)//第一种改变策略
{
    set(state);
    
    for(int i=0;i<4;i++)swap(a[1][i],a[2][i]);
    
    return get();
}

string change2(string state)//第二种改变策略
{
    set(state);
    
    int shang=a[1][3];
    int xia=a[2][3];
    
    for(int i=3;i>=0;i--)
    {
        a[1][i]=a[1][i-1];
        a[2][i]=a[2][i-1];
    }
    
    a[1][0]=shang;
    a[2][0]=xia;
    
    return get();
}

string change3(string state)//第三种改变策略
{
    set(state);
    
    int s=a[1][1];
    
    a[1][1]=a[2][1];
    
    a[2][1]=a[2][2];
    
    a[2][2]=a[1][2];
    
    a[1][2]=s;
    
    
    return get();
}


int bfs(string start,string end)
{
    if(start==end)return 0;
    
    queue<string>q;
    q.push(start);
    
    dist[start]=0;
    
    while(q.size())
    {
        auto t=q.front();
        
        q.pop();
        
        string state3[4];
        
        state3[1]=change1(t);
        
        state3[2]=change2(t);
        
        state3[3]=change3(t);
        
        for(int i=1;i<=3;i++)
         if(!dist[state3[i]])//如果我们这个位置没有遍历过的话,那就遍历一下
         {
             dist[state3[i]]=dist[t]+1;

             pre[state3[i]]={'A'+(i-1),t};
             
             q.push(state3[i]);
             
             if(state3[i]==end)return dist[end];//如果已经找到了终点,那就直接return
         }
    }
    
    return -1;//数据保证有解的,这里要不要都一样,但是为了严谨,还是随便return一个
}

int main()
{
    string start,end;
    
    for(int i=1;i<=8;i++)start+=char(i+'0');//初始化一下基本状态
    
    for(int i=1;i<=8;i++)
    {
        int x;
        scanf("%d",&x);
        
        end+=char(x+'0');//初始化一下特殊状态
    }
   
    int ans=bfs(start,end);
    
    printf("%d\n",ans);
    
    string res;
   
    while(end!=start)//找出对应的来源
    {
        res+=pre[end].l;
        end=pre[end].r;
    }
   
    
    reverse(res.begin(),res.end());
    
    if(ans>0)//题目要求
    cout<<res<<"\n";
    
    return 0;
}

好了,这也是我做的第一道最小步数的题目,写个题解纪念一下。

每篇博客后面都附上一句话。

我觉得我还是应该做自己目前最应该做的事情,那就是学习算法,而不是沉浸于你。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值