BFS+输出操作序列:魔板

原题链接:https://www.acwing.com/problem/content/1109/
在这里插入图片描述
这里不但要输出步数,还要记录字典序最小的操作序列。

字典序最小:每次操作的时候,优先选择A,再选择B,再选C。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
dist仅仅和输出步数有关,pre仅仅和输出操作序列有关。
char数组转换成string,方便比较和在map中存储。
string转换成char,方便进行变换。

第一次到的时候,顺次输出操作序列就是最小字典序的答案。可以用树的思想去考虑,每次按照ABC的顺序扩展一层,那么第一次到达的时候,一定是字典序最小的答案。

//woc看了y总的思路与代码实现,虽然比较繁琐
//但是一步一步来,是真的清晰!
//每次都按照ABC的顺序去搜,搜出来的就是字典序
#include<iostream>
#include<cstring>
#include<algorithm>
#include<unordered_map>
#include<queue>

using namespace std;

char g[2][4];
unordered_map<string, pair<char, string> > pre; //某个状态的前一个状态及对应的转移操作
unordered_map<string, int> dist;//每个状态和对应的步数

//string变换成矩阵
void set(string state)
{
    for(int i=0; i<4; i++) g[0][i]=state[i];
    for(int i=7, j=0; j<4; i--, j++) g[1][j]=state[i];
}

//矩阵变成string
string get()
{
    string res;
    for(int i=0; i<4; i++) res+=g[0][i];
    for(int i=3; i>=0; i--) res+=g[1][i];
    return res;
}

// 交换上下两行
string move0(string state)
{
    set(state);
    for(int i=0; i<4; i++) swap(g[0][i], g[1][i]);
    
    return get();
}

// 将最右边的一列插入到最左边;
string move1(string state)
{
    set(state);
    int v0=g[0][3], v1=g[1][3];
    for(int i=3; i>=1; i--)
    {
        g[0][i]=g[0][i-1];
        g[1][i]=g[1][i-1];
    }
    g[0][0]=v0, g[1][0]=v1;
    
    return get();
}

// 魔板中央对的4个数作顺时针旋转。
string move2(string state)
{
    set(state);
    int v=g[0][1];
    g[0][1]=g[1][1];
    g[1][1]=g[1][2];
    g[1][2]=g[0][2];
    g[0][2]=v;
    
    return get();
}


int bfs(string start, string end)
{
    if(start == end) return 0;
    
    // 队列中需要存string
    queue<string> q;
    q.push(start);
    dist[start]=0;
    
    while(q.size())
    {
        auto t=q.front();
        q.pop();
        
        string m[3];//记录三个转移状态
        m[0]=move0(t);
        m[1]=move1(t);
        m[2]=move2(t);
        
        // 对于三个可能的转移状态
        for(int i=0; i<3; i++)
            if(!dist.count(m[i]))//寻找没有被找过的状态
            {
                dist[m[i]]=dist[t]+1;
                // 存储{对应的操作,前一个状态}
                pre[m[i]]={'A'+i, t};
                // 入队
                q.push(m[i]);
                if(m[i]==end) return dist[end];
            }
    }
}

int main()
{
    int x;
    string start, end;
    for(int i=0; i<8; i++)
    {
        cin>>x;
        end+=char(x+'0');
    }
    
    // start是初始状态的字符串
    for(int i=1; i<=8; i++) start+=char('0'+i);
    
    int step=bfs(start, end);
    
    cout<<step<<endl;
    
    //记录操作
    string res;
    // 从后向前
    while(end != start)
    {
    	// res记录操作序列
        res+=pre[end].first;
        // end记录转移来的状态
        end=pre[end].second;
    }
    
    // 前后反转一下。
    reverse(res.begin(), res.end());
    
    if(step>0) cout<<res<<endl;
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值