小明的游戏

小明的游戏

题目传送门qwq

前言

说实话,刚拿到这道题,我因为懒得将坐标转换为一个编号,所以直接淦的二维最短路(明明二维最短路更麻烦好吧

在用二维做法A掉这题后,另外写了正常点的常规最短路

本篇题解将会介绍这两种做法

提醒

这道题给出的起点和终点的坐标都是从 \(0\)开始的

所以建图那些是从\(1\)\(N\)的话,请对起点终点坐标++(虽过不了样例也会提醒你这个\(point\))


算法证明

这题一看就是道搜索(\(DFS\)会被\(T\)掉,\(BFS\)的双端队列可以过)

但是这题也能跑最短路,将四个方向转换为连边,然后从\(1\)\(N\)求得最短距离即可


二维最短路

啊我真优秀

二维实现最短路还是调了一点时间的,这里就罗列一下我主要遇到的两个问题:

  1. 建图连边的时候,有向边的方向没有搞清楚(两种做法都要注意)

  2. 不知道\(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


posted @ 2020-07-04 12:14  Eleven谦  阅读(24)  评论(0编辑  收藏