做题随笔: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;
}

闲话

如果觉得有用,还请点个赞吧!

posted @ 2025-03-29 16:32  Tenil  阅读(19)  评论(0)    收藏  举报