每天一道蓝桥杯 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.这道题的背景应该是“八数码”,“八数码”是一个求给定状态到目的状态的最少操作次数,操作也是移动某个特定格子。