小明的游戏
小明的游戏
前言
说实话,刚拿到这道题,我因为懒得将坐标转换为一个编号,所以直接淦的二维最短路(明明二维最短路更麻烦好吧)
在用二维做法A掉这题后,另外写了正常点的常规最短路
本篇题解将会介绍这两种做法
提醒
这道题给出的起点和终点的坐标都是从 \(0\)开始的
所以建图那些是从\(1\)到\(N\)的话,请对起点终点坐标++(虽过不了样例也会提醒你这个\(point\))
算法证明
这题一看就是道搜索(\(DFS\)会被\(T\)掉,\(BFS\)的双端队列可以过)
但是这题也能跑最短路,将四个方向转换为连边,然后从\(1\)到\(N\)求得最短距离即可
二维最短路
(啊我真优秀)
二维实现最短路还是调了一点时间的,这里就罗列一下我主要遇到的两个问题:
-
建图连边的时候,有向边的方向没有搞清楚(两种做法都要注意)
-
不知道\(Dijkstra\)的优先队列对于二维最短路怎么进行存储和排序
然后我们再根据上面两个问题的解决,来讨论思路
- 有向边的方向
当前点向四个方向扩展,如果合法,那么有向边当然是当前点连向其他点
注意一下:向四个方向扩展时要判断是否越界!
- 优先队列维护二维最短路
在自定义结构体内使用重载运算符!
代码段:
struct node {
int val,tx,ty,net; //val是花费,tx是下一个点的横坐标,ty是纵坐标
bool operator < (const node &x )const { //重载运算符
return val>x.val;
}
} e[5200010];
priority_queue<node> q;
这样定义后,在跑二维最短路的时候就可以正常维护啦
- 二维最短路代码
#include <bits/stdc++.h>
using namespace std;
char s[5001][5001];
int n,m,sx,sy,ex,ey;
int tot,dis[5001][5001],vis[5001][5001],head[5001][5001];
struct node {
int val,tx,ty,net;
bool operator < (const node &x )const {
return val>x.val;
}
} e[5200010];
priority_queue<node> q; //重载运算符之后的优先队列
inline void add(int fx,int fy,int nx,int ny,int w) { //连边
e[++tot].tx=nx;
e[tot].ty=ny;
e[tot].val=w;
e[tot].net=head[fx][fy];
head[fx][fy]=tot;
}
inline void dijkstra(int fx,int fy) {
for(register int i=1;i<=n;i++) {
for(register int j=1;j<=m;j++) {
vis[i][j]=0;
dis[i][j]=20050206;
}
}
dis[fx][fy]=0;
q.push((node) {
0,fx,fy
});
while(!q.empty()) {
int x=q.top().tx;
int y=q.top().ty;
q.pop();
if(vis[x][y]) continue;
vis[x][y]=1;
for(register int i=head[x][y];i;i=e[i].net) {
int vx=e[i].tx;
int vy=e[i].ty;
if(dis[vx][vy]>dis[x][y]+e[i].val) {
dis[vx][vy]=dis[x][y]+e[i].val;
q.push((node) {
dis[vx][vy],vx,vy //这里dis[]就不用取反啦
});
}
}
}
}
int main() {
while(scanf("%d%d",&n,&m)) {
if(n==0&&m==0) break;
tot=0; //记得清零
memset(head,0,sizeof(head));
for(register int i=1;i<=n;i++) {
for(register int j=1;j<=m;j++) {
cin>>s[i][j];
}
}
scanf("%d%d%d%d",&sx,&sy,&ex,&ey);
sx++;sy++; //注意输入是从0开始
ex++;ey++;
for(register int i=1;i<=n;i++) { //如果给您的眼睛带来不适,我很抱歉QAQ
for(register int j=1;j<=m;j++) {
if(s[i-1][j]==s[i][j]&&i-1>=1) add(i,j,i-1,j,0);
if(s[i-1][j]!=s[i][j]&&i-1>=1) add(i,j,i-1,j,1);
if(s[i][j-1]==s[i][j]&&j-1>=1) add(i,j,i,j-1,0);
if(s[i][j-1]!=s[i][j]&&j-1>=1) add(i,j,i,j-1,1);
if(s[i+1][j]==s[i][j]&&i+1<=n) add(i,j,i+1,j,0);
if(s[i+1][j]!=s[i][j]&&i+1<=n) add(i,j,i+1,j,1);
if(s[i][j+1]==s[i][j]&&j+1<=m) add(i,j,i,j+1,0);
if(s[i][j+1]!=s[i][j]&&j+1<=m) add(i,j,i,j+1,1);
}
}
dijkstra(sx,sy);
printf("%d\n",dis[ex][ey]);
}
return 0;
}
常规最短路
讲真,常规最短路其实就是上面二维最短路的优化(二维最短路属于不动脑子,直接给什么做什么,常规的最短路就需要转换,但是转换之后代码更简便也更容易实现)
- 坐标转换为编号
为什么要将坐标转换为编号?因为这样我们就将维护二维最短路改成常规的最短路了,接下来就不用想什么重载运算符后用优先队列维护三个值了
- 常规最短路代码\(Code\)
因为常规最短路的思路真的很简单,就是跑板子,所以直接给代码啦qwq
#include <bits/stdc++.h>
using namespace std;
char s[510][510];
int n,m,sx,sy,ex,ey,tot,cnt;
deque<int> q;
int sum[510][510],dis[520010],vis[520010],head[520010];
struct node {
int to,net,val;
} e[520010];
inline void add(int u,int v,int w) {
e[++tot].to=v;
e[tot].val=w;
e[tot].net=head[u];
head[u]=tot;
}
inline void spfa(int s) {
for(register int i=1;i<=cnt;i++) {
vis[i]=0;
dis[i]=20050206;
}
dis[s]=0;
vis[s]=1;
q.push_back(s);
while(!q.empty()) {
int x=q.front();
q.pop_front();
vis[x]=0;
for(register int i=head[x];i;i=e[i].net) {
int v=e[i].to;
if(dis[v]>dis[x]+e[i].val) {
dis[v]=dis[x]+e[i].val;
if(!vis[v]) {
vis[v]=1;
if(!q.empty()&&dis[q.front()]<dis[v]) q.push_back(v);
else q.push_front(v);
}
}
}
}
}
int main() {
while(scanf("%d%d",&n,&m)) {
if(n==0&&m==0) break;
tot=0;cnt=0;
memset(sum,0,sizeof(sum));
memset(head,0,sizeof(head));
for(register int i=1;i<=n;i++) {
for(register int j=1;j<=m;j++) {
cin>>s[i][j];
sum[i][j]=++cnt; //用编号代替坐标
}
}
scanf("%d%d%d%d",&sx,&sy,&ex,&ey);
sx++;sy++;ex++;ey++;
for(register int i=1;i<=n;i++) {
for(register int j=1;j<=m;j++) { //如果又给您的眼睛带来不适,我再次感到很抱歉QAQ
if(s[i][j]==s[i-1][j]&&i-1>0) add(sum[i][j],sum[i-1][j],0);
if(s[i][j]!=s[i-1][j]&&i-1>0) add(sum[i][j],sum[i-1][j],1);
if(s[i][j]==s[i][j-1]&&j-1>0) add(sum[i][j],sum[i][j-1],0);
if(s[i][j]!=s[i][j-1]&&j-1>0) add(sum[i][j],sum[i][j-1],1);
if(s[i][j]==s[i+1][j]&&i+1<=n) add(sum[i][j],sum[i+1][j],0);
if(s[i][j]!=s[i+1][j]&&i+1<=n) add(sum[i][j],sum[i+1][j],1);
if(s[i][j]==s[i][j+1]&&j+1<=m) add(sum[i][j],sum[i][j+1],0);
if(s[i][j]!=s[i][j+1]&&j+1<=m) add(sum[i][j],sum[i][j+1],1);
}
}
spfa(sum[sx][sy]);
printf("%d\n",dis[sum[ex][ey]]);
}
return 0;
}
嗯...写完了...
最后,如果这篇题解有任何错误或您不懂的地方,欢迎下方留言区评论,我一定会及时回复、改正
谢谢各位dalao啊qwq
From:Eleven谦