蓝桥杯_PREV-19_九宫重排 【双广搜】

题目链接

题目描述

本题就类似于小游戏拼图,将九宫格中扣去一块,可以导致拼图可以移动,现在问你,给定一个图形,是否可以通过移动得到另一种形状。如果可以,输出最小步数,否则输出-1。
这里写图片描述这里写图片描述

解题思路

首先我们可以看出,移动的方向,只能是上下左右四个方向移动,并且只可以将有数字(‘1’ ~ ‘8’)的位置移向空格(‘.’)的位置。反过来可以看出,只有’.’可以上下左右四个方向移动。

我们可以把九宫格3*3的图一维来表示。这里如果基础好的就可以直接跳过了。
这里写图片描述

如图我们可以把 3*3 的空间压成 1*9的空间,这时候我们在三维空间中上下左右的移动,就可以在一维空间中,将下标值 -3,+3,-1,+1来表示。
比如:1图 空格移动到6的位置,就可以在2图中将空格向左移动3格即下标-3 就到了6的位置

所以我们就可以把一个三维图形用一个字符串表示,即一个字符串就可以表示一个九宫格的状态。

首先我的思路:步数最小,优先考虑广搜,我们把开始的状态通过题目中所规定的移动规则,不断一层一层的搜索,如果在搜索的过程中找到了终止的状态,那就直接输出步数,注意边搜索边标记,通过map<string,int>来对字符串进行标记和查询是否标记。
但是很不幸,TEL超时了,只过了60%的数据。
考虑双广搜的思想,起点和终点一起搜索,定义两个map映射:M1、M2,分别表示起点状态到当前状态的步数和终点状态到当前状态的步数,一起搜索的过程中,如果发现一个状态在M1和M2中都不为0,则把其对接一起。然后就A~啦,啊哈哈!

代码部分

#include <cstdio>  
#include <cstring>  
#include <string>  
#include <iostream>  
#include <queue>
#include <map>

using namespace std;  

map <string, int> M1;
map <string, int> M2;
typedef pair <string, int> P;
int dir[4] = {1, -1, 3, -3};
string s, e; // s 代表起始, e 代表结束 
bool judge(int x)
{
    if(x < 0 || x > 8) 
        return false;
    else
        return true;
}
int bfs()
{
    P pre1, nex1, pre2, nex2;
    pre1.first = s;
    pre1.second = 0;
    pre2.first = e;
    pre2.second = 0;

    queue <P> Q1; 
    queue <P> Q2;

    M1[s] = 0;
    M2[s] = 0;

    Q1.push(pre1);
    Q2.push(pre2);

    while(!Q1.empty())
    {
        pre1 = Q1.front();
        Q1.pop();
        pre2 = Q2.front();
        Q2.pop();

        string ts1 = pre1.first;
        int tn1 = pre1.second;
        string ts2 = pre2.first;
        int tn2 = pre2.second;

        int it1 = ts1.find('.');
        int it2 = ts2.find('.');

        if(ts1 == e)
        {
            return tn1;
        }

        for(int i = 0; i < 4; ++ i)
        {
            int t1 = it2 + dir[i];

            string t2 = ts2;
            char t3 = t2[it2];
            t2[it2] = t2[t1];
            t2[t1] = t3;

            int t4 = tn2 + 1;
            if((i == 0 && (it2 == 2 || it2 == 5))||(i == 1 && (it2 == 3 || it2 == 6)))
                continue;
            if(judge(t1))
            {
                if(M2[t2])
                    continue;
                M2[t2] = tn2 + 1;
                if(M1[t2])
                    return M1[t2] + M2[t2];
                nex2.first = t2;
                nex2.second = t4;
                Q2.push(nex2);
            }
        }


        for(int i = 0; i < 4; ++ i)
        {
            int t1 = it1 + dir[i];

            string t2 = ts1;
            char t3 = t2[it1];
            t2[it1] = t2[t1];
            t2[t1] = t3;

            int t4 = tn1 + 1;
            if((i == 0 && (it1 == 2 || it1 == 5))||(i == 1 && (it1 == 3 || it1 == 6)))
                continue;
            if(judge(t1))
            {
                if(M1[t2])
                    continue;
                M1[t2] = tn1 + 1;
                if(M2[t2])
                    return M1[t2] + M2[t2];
                nex1.first = t2;
                nex1.second = t4;
                Q1.push(nex1);
            }
        }

    }
    return -1;
} 
int main()  
{  
    int ans;
    cin >> s >> e;
    ans = bfs();
    if(ans == -1)
        cout << "-1" << endl;
    else
        cout << ans << endl;
    return 0;  
}   
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值