zoukankan      html  css  js  c++  java
  • 小明的游戏

    小明的游戏

    题目传送门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
    ",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
    ",dis[sum[ex][ey]]);
    	}
    	return 0;
    }
    

    嗯...写完了...

    最后,如果这篇题解有任何错误或您不懂的地方,欢迎下方留言区评论,我一定会及时回复、改正

    谢谢各位dalao啊qwq


  • 相关阅读:
    Adobe PS
    深入学习二叉树(04)平衡二叉树
    深入学习二叉树(03)二叉查找树
    C 知识点
    实战【docker 镜像制作与使用】
    从0到1了解 CI/CD
    单例模式的几种实现方式,使用场景以及优缺点
    设计模式之策略模式浅谈以及简单例子
    WPF几种渐变色
    Linq学习以及简单用法
  • 原文地址:https://www.cnblogs.com/Eleven-Qian-Shan/p/13234481.html
Copyright © 2011-2022 走看看