宫格状态求解类问题 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;
}
}