每天一道蓝桥杯 Day3 移动字母

 

题意:

 

思考过程:

首先观察这道题的数据范围不是很大,一共才6个位置,并且每个位置只出现一次。

那么不考虑合法,只算总状态的话就是7*6*5*4*3*2*1=720

状态数很少,启发我们可以用搜索!

那么搜索是用dfs还是bfs?

bfs有一个特性:从s出发,第一次搜索到状态t时所用的步数,肯定是所需的最小操作次数。

因为bfs每次扩展时只多走一步,假设第一次到某个点所用步数为c,最优解为c'

如果c'<=c,bfs每次会把能扩展的都扩展掉,则一定会更早扩展到c'而不是c。

这个性质有一个直观的呈现:图中蓝色的是起点,绿色的是终点,红色的是障碍物,粉色的是最优解。

而dfs则不一定,有可能存在更优的解法,要全部搜索完才能找到最优解。

再回头看这道题,由于只询问是否可达,不问最优解的话,那dfs或者bfs都可以。

用dfs实现:

实现上也有一些小技巧。

1.读入的是字符串,但是实际上移动时又是在一个2*3的字符数组上向上下左右移动。

直接分位置讨论的话由于数据小也是可行的,不过可以考虑引入方向数组

dx[4]={0,0,-1,1},dy[4]={1,-1,0,0}

//假设当前在的位置是(x,y),拓展时只需要

for(int i=0;i<4;i++){
     int tx=x+dx[i],ty=y+dy[i];  
}

就可以省去分类讨论的麻烦。

2.像这种终点明确,起点很多的询问,如果操作是可逆的,就可以考虑从终点出发,预处理出所有可达的状态。

具体地,我们可以从"ABCDE*"出发,dfs出所有能到的状态,把能到的状态打标记。

处理询问时,只需要查询这个状态有没有被搜索过。

代码:

#include<bits/stdc++.h>
using namespace std;
int dx[4]={0,0,-1,1};
int dy[4]={-1,1,0,0};
char a[3][7];
map<string,int>mp;
int cnt=0;
string check( ){
    string s="";//把数组再转回字符串,方便用map存储
    for(int i=1;i<=2;i++)
     for(int j=1;j<=3;j++){
        s=s+a[i][j];
     }
    return s;
}
void dfs(int x,int y){
    if(mp[check( )]) return;
    mp[check( )]=1;//对当前访问到的状态打标记,防止之后又被搜索,时间复杂度将会很大。
    for(int i=0;i<4;i++){
        int tx=x+dx[i],ty=y+dy[i];
        if(tx<=2&&tx>=1&&ty<=3&&ty>=1){//拓展的格子肯定不能越界
            swap(a[tx][ty],a[x][y]);
            dfs(tx,ty);
            swap(a[tx][ty],a[x][y]);//回溯
        } 
    } 
}
void solve(){
    a[1][1]='A';a[1][2]='B';a[1][3]='C';//转化成二维数组
    a[2][1]='D';a[2][2]='E';a[2][3]='*';
    dfs(2,3);
}
int main(){
    solve();//预处理出所有终点能到的状态,只要终点能到,那么它也可以到终点。
    int t;cin>>t;
    for(int i=1;i<=t;i++){
        if(t==1) cout<<1<<endl;
        else cout<<mp[check()]<<endl;
    }
}

题外话:

1.这道题官方数据出锅啦,只要输出1就能过。

2.这道题的背景应该是“八数码”,“八数码”是一个求给定状态到目的状态的最少操作次数,操作也是移动某个特定格子。

posted @ 2024-03-09 23:04  liyishui  阅读(25)  评论(0编辑  收藏  举报