P7284 [COCI2020-2021#4] Patkice II 题解

广搜好题

题目大意

起点是 "o" , 终点是 "x" ,"^ v < >" 表示四种洋流,鸭子进入洋流后就可以沿着该方向移动距离, "." 是平静的海面,鸭子进入这里就会停止。问我们需要至少改变多少个字符才能使鸭子从起点走向终点,并且打印出改变字符后的地图。

思路

一般求最短路径的算法就是 BFS ,所以我们思考如何去使用 BFS : 四个方向走,如果我们要走的方向和洋流方向一致,那么就不需要改变洋流方向,权值为 \(0\) ; 如果我们要走的方向和洋流方向不一致,那么在走的过程中我们需要改变洋流方向,权值为 \(1\)

想到这一步,我们就可以考虑优先队列和双端队列了,因为权值为 \(0\) 的情况一定是比权值为 \(1\) 的情况更优的(这里我用的双端队列),所以把权值为 \(0\) 的情况从队头插入,把权值为 \(1\) 的情况从队尾插入。这样,更好的情况一定是在前面的。

这个题的另一个问题就在于如何打印出新的地图。为了解决这个问题,我们给一个数组来记录前驱,如果从这个地方走更优,我们就记录他的前驱。最后根据这个点和前驱点的位置关系来更改地图即可。

时间复杂度和空间复杂度都为 \(O(r \times s)\)

代码实现

#include<bits/stdc++.h> 
#define end endd
#define map mapp
const int INF=2147483647;
using namespace std;

char map[2005][2005];
int r,s;
int startx,starty,endx,endy;
struct node
{
	int x,y;
}now,end,pre[2005][2005];/*pre数组记录前驱*/
int bu[2005][2005];/*bu数组记录到达某个点需要更改字符的个数*/

int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
char direct[4]={'v','>','^','<'};/*四个方向,要注意和洋流对应*/

deque <node> q;/*双端队列*/

void bfs(int x,int y)
{
	now.x=x,now.y=y;
	bu[x][y]=0;
	q.push_back(now);
	while(!q.empty())
	{ 
		now=q.front();	
		q.pop_front();
		if((now.x==endx && now.y == endy) || bu[now.x][now.y] >= bu[endx][endy]) continue;/*剪枝,如果到达终点了或者到达该点的步数已经超过了此刻到达终点的步数了,那么就不需要再往后做了*/
		for(int i=0;i<4;i++)
		{
			int xx=now.x+dx[i];
			int yy=now.y+dy[i];	
			if(xx<1 || xx>r || yy<1 || yy>s) continue;
			int step = (map[now.x][now.y]==direct[i] || map[now.x][now.y] == 'o') ? 0 : 1;/*如果是起点或者洋流方向与我们要走的方向一致,那么权值就是0,否则是1*/
			if(bu[xx][yy] > bu[now.x][now.y] + step)
			{
				bu[xx][yy] = bu[now.x][now.y] + step;/*能更新就更新*/
				pre[xx][yy]=now;/*记录前驱*/
				end.x=xx,end.y=yy;
				if(step == 0) q.push_front(end);/*如果权值是0,较优,从队头插入,否则从队尾插入*/
				else q.push_back(end);
			}
		}
	}
}

void change(int kx,int ky)
{
	int px=pre[kx][ky].x,py=pre[kx][ky].y;
	while(px!=startx || py!=starty)/*要注意是前驱不为起点,如果用错会修改掉起点的方向*/
	{
		if(px==kx+1) map[px][py]='^';/*更新地图*/
		if(px==kx-1) map[px][py]='v';
		if(py==ky+1) map[px][py]='<';
		if(py==ky-1) map[px][py]='>';
		kx=px,ky=py; 
		px=pre[kx][ky].x,py=pre[kx][ky].y;
	}
}

signed main()
{
	ios::sync_with_stdio(false);
	cin >> r >> s;
	for(int i=1;i<=r;i++)
		for(int j=1;j<=s;j++)
		{
			bu[i][j]=INF;
			cin >> map[i][j];
			if(map[i][j] == 'o') startx=i,starty=j;
			if(map[i][j] == 'x') endx=i,endy=j;	/*记录起点终点位置*/
		}
	bfs(startx,starty);/*开始bfs*/
	cout<<bu[endx][endy]<<endl;
	change(endx,endy);/*更改地图*/
	for(int i=1;i<=r;i++)
	{
		for(int j=1;j<=s;j++)
			cout<< map[i][j];
		cout << endl;/*输出*/
	}
	return 0;
}
posted @ 2023-05-25 21:43  Bloodstalk  阅读(15)  评论(0编辑  收藏  举报