宫格状态求解类问题

宫格状态求解类问题 2022.3.19

题目实例1 蓝桥杯2012年省赛 移动字母

原题目:https://www.lanqiao.cn/problems/280/learning/

题目描述

2x3=6 个方格中放入 ABCDE 五个字母,右下角的那个格空着。如下图所示。

在这里插入图片描述

和空格子相邻的格子中的字母可以移动到空格中,比如,图中的 C 和 E 就可以移动,移动后的局面分别是:

A B
D E C

A B C
D   E

为了表示方便,我们把 6 个格子中字母配置用一个串表示出来,比如上边的两种局面分别表示为:

AB*DEC
ABCD*E

题目的要求是:请编写程序,由用户输入若干表示局面的串,程序通过计算,输出是否能通过对初始状态经过若干次移动到达该状态。可以实现输出 1,否则输出 0。初始状态为:ABCDE*

输入

先是一个整数 n,表示接下来有 n 行状态。

输出

程序输出 n 行 1 或 0。

样例输入

3
ABCDE*
AB*DEC
CAED*B

样例输出

1
1
0

分析

在这里插入图片描述

若第一步选择走上面:AB* DEC
若第一步选择走左边:A* CDBE -> AC* DBE

可见,空格位置一定时,宫格内配置不一定相同,但是同一条深度路径上(即前几步都走的一样的情况下)空格位置一定,方块内配置一定。

题解

//DFS_By_Hive
#include <bits/stdc++.h>
using namespace std;

#define Max 10
char mp[Max] = {0};
char str[Max] = "ABCDE*";

int vis[10] = {0};
int n;
int h = 0, l = 0; //空格所在行列
int isok = 0;     //方案是否可行

int dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};

void dfs(int x, int y)
{
    int xx, yy;

    if (!strcmp(mp, str))
    { //与目标状态相同
        isok = 1;
        return;
    }
    else
    {
        for (int i = 0; i < 4; i++)
        {
            xx = x + dir[i][0];
            yy = y + dir[i][1];
            if (xx >= 0 && xx < 2 && yy >= 0 && yy < 3 && !vis[xx * 3 + yy])
            { //用vis剪枝,若已尝试过空格在此处,直接跳过
                swap(mp[xx * 3 + yy], mp[xx * 3 + yy]);
                vis[xx * 3 + yy] = 1;

                dfs(xx, yy);
                
                //回溯
                swap(mp[xx * 3 + yy], mp[x * 3 + y]);
                vis[xx * 3 + yy] = 0;
            }
        }
    }
}
int main()
{
    scanf("%d\n", &n);
    while (n--)
    {
        memset(vis, 0, sizeof(vis));
        memset(mp, 0, sizeof(mp));
        scanf("%s", mp);
        for (int i = 0; mp[i]; i++)
        {
            if (mp[i] == '*')
            {
                h = i / 3;
                l = i % 3;
            }
        }

        isok = 0;
        dfs(h, l);

        printf("%d\n", isok);
    }
    return 0;
}

题目实例2 蓝桥杯2013年国赛 九宫重排

原题目:https://www.lanqiao.cn/problems/261/learning/

题目描述

如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。

在这里插入图片描述

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

输入

输入第一行包含九宫的初态,第二行包含九宫的终态。

输出

输出最少的步数,如果不存在方案,则输出-1。

样例输入

12345678. 
123.46758 

样例输出

3

样例分析

12345678.

(1) 1234567.8
(2) 1234.6758
(3) 123.46758

123.46758
//最开始看没看题要求求的是最少操作步骤 只用DFS判断了可行  如下
#include<bits/stdc++.h>
using namespace std;

string s; //初态
string t; //终态
bool isok=false;
const int N=3; //宫格边长


int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
int vis[N*N+5];

inline bool bound(int x,int y){
    if(x<0||x>=N||y<0||y>=N){
        return false;
    }
    return true;
}

void DFS(int pos,int step){
    if(s==t){
        isok=true;
        return;
    }

    int x=pos/N;
    int y=pos%N;

    for(int i=0;i<4;i++){
        int xx=x+dx[i];
        int yy=y+dy[i];
        int next_blank=xx*3+yy; //空格下一个绝对位置
        if( !vis[next_blank] && bound(xx,yy) ){

            vis[next_blank]=1;
            swap(s[next_blank],s[pos]);

            DFS(next_blank,step+1);
            
            //回溯
            vis[next_blank]=0;
            swap(s[next_blank],s[pos]);
            
        }
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin>>s>>t;
    int pos=s.find('.'); //空格初始位置
    vis[pos]=1;
    DFS(pos,0);
    cout<<isok<<endl;
}
 

//BFS By_Hive
#include<bits/stdc++.h>
using namespace std;

string s; //初态
string t; //终态
bool isok=false;
const int N=3; //宫格边长


int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
int vis[N*N+5];

struct node{
    int pos; //空格位置
    int step; //变换次数
    string tmp; //中间状态
};

set<string> st; //存储中间状态,用于去重
queue<node> q;

inline bool bound(int x,int y){
    if(x<0||x>=N||y<0||y>=N){
        return false;
    }
    return true;
}

void BFS(){

    while(!q.empty()){

        node f=q.front();
        q.pop();
        int pos=f.pos;
        int x=pos/N;
        int y=pos%N;
        string str=f.tmp;
        st.insert(str); //存储本中间状态

        if(f.tmp==t){
            cout<<f.step<<endl;
            isok=true;
            break;
        }

        for(int i=0;i<4;i++){
            int xx=x+dx[i];
            int yy=y+dy[i];
            int next_blank=xx*3+yy; //空格下一个绝对位置

            if( bound(xx,yy) ){
                
                
                string tmp2=str; 
                //把当前队首节点的字符串复制一份,不可直接修改str,
                //否则同轮次其他方向使用的是此方向已经修改过的串
                //造成答案错误
                
                swap(tmp2[next_blank],tmp2[pos]);
                if(st.count(tmp2)!=0){ //如果已有此种状态,跳过
                    continue;
                }

                node tmp;
                tmp.pos=next_blank;
                tmp.step=f.step+1;
                tmp.tmp=tmp2;

                q.push(tmp);
            }
        }
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin>>s>>t;
    int pos=s.find('.'); //空格初始位置

    vis[pos]=1;

    node tmp;
    tmp.pos=pos;
    tmp.tmp=s;
    tmp.step=0;
    q.push(tmp);

    BFS();

    if(!isok){
        cout<<"-1"<<endl;
    }
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alveus

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值