做题随笔:P10485
Solution
题意
给定 \((R-2) \times (C-2)\) 棋盘(棋盘可能不全)和起点、终点,问能否将 \(1 \times 1 \times 2\) 棋子从起点以特定方式移动到终点,若可以求最小步数。
移动方式:
棋子有直立、竖躺和横躺三种状态,初始为直立,要求到终点时也为直立。
移动时,进行滚动:
直立:左右移动变横躺,上下移动变竖躺
竖躺:左右移动不变,上下移动变直立
横躺:左右移动变直立,上下移动不变
要求棋子必须完全在棋盘上,且不能在“易碎单元格”上直立。
分析 & 实现
求最少步数,典型 bfs 特征了,只是本题的状态比较麻烦。
将上述三状态分别记为:直立(0)、竖躺(1)和横躺(2)。竖躺时,位置记上方一格;横躺时记左侧一格。(这样设计可以让你在读入和特判时都只需将坐标加一,相对统一一点,不容易写错)
先写出转移数组(右、下为正方向):
//ort=0:立 1:竖躺(上面) 2:横躺(左边) dest:0123 上下左右
const int dx[3][4]{{0,0,-2,1},{0,0,-1,1},{0,0,-1,2}};
const int dy[3][4]{{-2,1,0,0},{-1,2,0,0},{-1,1,0,0}};
const int nxtort[3][4]{{1,1,2,2},{0,0,1,1},{2,2,0,0}};
然后转移的时候注意每个朝向的特判:
bool ok=1;
switch (nxt.o) {
case 0://不能在“易碎单元格”上直立
if(board[nxt.x][nxt.y]==2) ok=0;
break;
case 1://棋子必须完全在棋盘上
if(!board[nxt.x][nxt.y+1]) ok=0;
break;
case 2:
if(!board[nxt.x+1][nxt.y]) ok=0;
break;
}
if(!ok) continue;
然后本题其实也就写完了(乐)
Code
读入可能有点抽象,还请见谅。
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cmath>
#include <queue>
#include <cstring>
using namespace std;
typedef long long ll;
ll fr() {
ll x=0,f=1;char c=getchar();
while(!isdigit(c)) {
if(c=='-') f=-1;
c=getchar();
}
while(isdigit(c)) {
x=(x<<3)+(x<<1)+(c^48);
c=getchar();
}
return x*f;
}
const int maxn=501;
//ort=0:立 1:竖躺(上面) 2:横躺(左边) dest:0123 上下左右
const int dx[3][4]{{0,0,-2,1},{0,0,-1,1},{0,0,-1,2}};
const int dy[3][4]{{-2,1,0,0},{-1,2,0,0},{-1,1,0,0}};
const int nxtort[3][4]{{1,1,2,2},{0,0,1,1},{2,2,0,0}};
int r,c,sx,sy,so,ex,ey;
int board[maxn][maxn];
//0空 1硬 2碎
bool vis[maxn][maxn][3];
struct att{
int x,y,o,step=0;
};
int main() {
r=fr();c=fr();
while(r) {
sx=sy=so=ex=ey=0;
queue<att> q;
memset(vis,0,sizeof(vis));
memset(board,0,sizeof(board));//多测要清空!
string str;getline(cin,str);//先吃一行#
bool flag=0;
for(int i = 1; i <= r-2; i++) {
getchar();//行首#
char cc;
for(int j = 1; j <= c-2; j++) {
cc=getchar();
if(cc=='#') continue;
if(cc=='.') board[j][i]=1;
else if(cc=='E') board[j][i]=2;
else if(cc=='X') {
if(!sx) {
sx=j,sy=i;
}
else {
if(sx==j-1) so=2;
if(sy==i-1) so=1;
}
board[j][i]=1;
}
else if(cc=='O') {
ex=j,ey=i;board[j][i]=1;
}
}
getchar();getchar();//行末#和换行符
}
getline(cin,str);
q.push((att){sx,sy,so,0});
while(!q.empty()) {
att now=q.front();
q.pop();
if(now.x==ex&&now.y==ey&&now.o==0) {
printf("%d\n",now.step);flag=1;
break;
}
for(register int i = 0; i < 4; i++) {
att nxt=(att){now.x,now.y,now.o,now.step+1};
nxt.x+=dx[now.o][i];
nxt.y+=dy[now.o][i];
nxt.o=nxtort[now.o][i];
if(nxt.x<1||nxt.x>c-2||nxt.y<1||nxt.y>r-2||!board[nxt.x][nxt.y]) continue;
if(vis[nxt.x][nxt.y][nxt.o]) continue;
bool ok=1;
switch (nxt.o) {
case 0:
if(board[nxt.x][nxt.y]==2) ok=0;
break;
case 1:
if(!board[nxt.x][nxt.y+1]) ok=0;
break;
case 2:
if(!board[nxt.x+1][nxt.y]) ok=0;
break;
}
if(!ok) continue;
vis[nxt.x][nxt.y][nxt.o]=1;
q.push(nxt);
}
}
if(!flag) printf("Impossible\n");
r=fr();c=fr();
}
return 0;
}
闲话
如果觉得有用,还请点个赞吧!