P4554 小明的游戏 题解

写在前面

看了所有题解,都是用最短路或双端队列的 bfs,于是就有了本文:用了优先队列的 bfs。感觉和这题很像,但本题更容易。

算法分析

题目大意

给出一个 $n \times m$ 的矩阵,问从起点($x_1$,$y_1$)走到终点($x_2$,$y_2$)的最少花费是多少?只能向上下左右四个方向移动,定义在两个不同类型的格子之间移动需要花费 $1$。

思路

看到这题第一反应就是用 bfs,但是直接 bfs 是错误的,因为最先到达终点的路线不一定最优。举个例子就懂了(下面用 $0$ 表示 @,$1$ 表示 #):

0 1 0
0 1 0
0 0 0

起点为 $0,0$,终点为 $0,2$。那最先到达终点的路线为 $0,0 \to 0,1 \to 0,2$,花费 $2$。但是最优路线应该是 $0,0 \to 1,0 \to 2,0 \to 2,1 \to 2,2 \to 1,2 \to 0,2$,花费 $0$。

这里我们可以用贪心策略:每次从队列中取出新的点时,选择最小的那一个,并将这个点标记一下(不重复走),同时使用堆优化(这里有点像 dijkstra)。把普通队列改为优先队列,以走到当前这一格的花费进行排序,花费的排在前面,这样就可以优先搜索花费更少的格子。

为什么这样做是对的?
因为花费不可能减少,所以每次新生成的点中花费最少的一定是当前的最优,那么以这个点向外扩散所产生的点应最优,以此类推,那么按这样搜索第一个到达终点的路线一定最优。

代码参考

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=510;
int n,m;
bool a[N][N];		//存矩阵:@ 为 1,# 为 0 
bool vis[N][N];		//标记数组 
int d[4][2]={1,0,-1,0,0,1,0,-1};
struct node{
	int x,y;		//当前格子的位置 
	int val;		//走到当前格子的花费 
};
bool operator <(const node &a,const node &b){	//花费小的排在前面 
	return a.val>b.val;
}
int bfs(int h1,int l1,int h2,int l2){
	priority_queue<node> q;
	vis[h1][l1]=1;
	q.push({h1,l1,0});
	while(!q.empty()){
		node t=q.top();
		int x=t.x, y=t.y, val=t.val;
		if(x==h2&&y==l2) return val; 
		q.pop();
		for(int i=0;i<4;++i){
			int xx=x+d[i][0];
			int yy=y+d[i][1];
			if(xx<0||xx>=n||yy<0||yy>=m) continue;		//判断边界 
			if(vis[xx][yy]) continue;
			vis[xx][yy]=1;
			if(a[x][y]==a[xx][yy])
				q.push({xx,yy,val});
			else 
				q.push({xx,yy,val+1});
		}
	}
}
signed main(){
	while(cin>>n&&cin>>m){
		if(n==0&&m==0) break;
		memset(a,0,sizeof a);		//一定要重置数组 
		memset(vis,0,sizeof vis);
		for(int i=0;i<n;++i)		//注意:从 0 开始 
			for(int j=0;j<m;++j){
				char x; cin>>x;
				if(x=='@') a[i][j]=1; 
			}
		int x,y,h,l;
		cin>>x>>y>>h>>l;
		cout<<bfs(x,y,h,l)<<endl;
	}
	
    return 0;
}
posted @ 2025-07-27 10:13  雨梦-H  阅读(48)  评论(0)    收藏  举报