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;
}

浙公网安备 33010602011771号