欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

备战蓝桥杯决赛----坚持第四天!!!

程序员文章站 2022-05-24 09:41:17
...

      又是一个深夜。。。昨天说今天要找一些关于BFS和DFS的练习题,今天当然是要怎么做。但是,我仅仅做了两道题,全来自于蓝桥杯练习题库。再看看所用时间,发现自己的效率是真的不高。。(一直以来的问题,还没有找到好的办法解决)

     那就说下这两道题目吧,说完还要洗洗睡觉。。。

     首先是一道关于BFS的练习题

问题描述
  学霸抢走了大家的作业,班长为了帮同学们找回作业,决定去找学霸决斗。但学霸为了不要别人打扰,住在一个城堡里,城堡外面是一个二维的格子迷宫,要进城堡必须得先通过迷宫。因为班长还有妹子要陪,磨刀不误砍柴功,他为了节约时间,从线人那里搞到了迷宫的地图,准备提前计算最短的路线。可是他现在正向妹子解释这件事情,于是就委托你帮他找一条最短的路线。
输入格式
  第一行两个整数n, m,为迷宫的长宽。
  接下来n行,每行m个数,数之间没有间隔,为0或1中的一个。0表示这个格子可以通过,1表示不可以。假设你现在已经在迷宫坐标(1,1)的地方,即左上角,迷宫的出口在(n,m)。每次移动时只能向上下左右4个方向移动到另外一个可以通过的格子里,每次移动算一步。数据保证(1,1),(n,m)可以通过。
输出格式
  第一行一个数为需要的最少步数K。
  第二行K个字符,每个字符∈{U,D,L,R},分别表示上下左右。如果有多条长度相同的最短路径,选择在此表示方法下字典序最小的一个。
样例输入
Input Sample 1:
3 3
001
100
110

Input Sample 2:
3 3
000
000
000
样例输出
Output Sample 1:
4
RDRD

Output Sample 2:
4
DDRR
数据规模和约定
  有20%的数据满足:1<=n,m<=10
  有50%的数据满足:1<=n,m<=50
  有100%的数据满足:1<=n,m<=500。

思路:通过bfs寻找一条可从起点到达终点的最短路径,但是这里附件了一个条件:如果有多条长度相同的最短路径,选择在此表示方法下字典序最小的一个。这才是值得思考的地方,既然需要字典序最小,那么我们首先对四个方向进行字典序排序,排序结果为:DLRU。那么我们在进行bfs搜索时,按照这个排序方向的先后顺序,进行搜索,这样最终确定的一定是一个字典序最小的路径。

附程序代码及重要注释:

#include <iostream>  
#include <cstdio>  
#include <cstring>  
#include <algorithm>  
#include <queue>  
using namespace std;
const int MAX=501;//有100%的数据满足:1<=n,m<=500。因为我们使用时从1,1开始,所以数组长度为501  
struct pos{
	int x;
	int y;
	int sum;
}; 
int dict[4][2]={{1,0},{0,-1},{0,1},{-1,0}};//这里数组表示的是与字典序对应操作DLRU所产生的结果,二维数组中两个元素分别代表行(1下移0不动-1上移)和列(1右移0不动-1左移)
                                          //的变化。其实很好理解,想两遍就通了
char str[5]="DLRU";//详细说明见下面常识一
int pre[MAX][MAX];
char temp[MAX][MAX];
int vis[MAX][MAX];
int n;
int m;
void bfs(){
	queue<pos> q;
	memset(vis,0,sizeof(vis));
	pos p;
	p.x=1;
	p.y=1;
	p.sum=0;
	q.push(p);
	vis[1][1]=1;
	pre[1][1]=-1;//设置其实点的pre为-1,不存在 
	while(!q.empty()){
		p=q.front();
		q.pop();
		if(p.x==n&&p.y==m){
			cout<<p.sum<<endl;
			return;//此时一定要return空结束循环 
		}
		pos pp;
		for(int i=0;i<4;i++){
			pp.x=p.x+dict[i][0];
			pp.y=p.y+dict[i][1];
			pp.sum = p.sum+1;
			if(pp.x>=1&&pp.x<=n&&pp.y>=1&&pp.y<=m&&temp[pp.x][pp.y]=='0'&&vis[pp.x][pp.y]==0){
				vis[pp.x][pp.y]=1;
				pre[pp.x][pp.y]=i;
				q.push(pp);
			}
		}
	}
}
void path(int x,int y){
	if(x==1&&y==1){
		return;
	}
	path(x-dict[pre[x][y]][0],y-dict[pre[x][y]][1]);//输出时,我们反向寻找,从终点寻找到起点
	cout<<str[pre[x][y]];//递归到起点位置,每次返回上一层时,打印对应操作字符,即又变成从起点到终点的动作
}
int main () {  
    cin>>n>>m;
    getchar();
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=m;j++){
    		cin>>temp[i][j];
		}
		getchar();//详细说明见下面常识二
	}
	bfs();
	path(n,m);
    return 0;  
} 

另外,想在此题下面补充两个常识(其实就是之前忘记的知识点)

常识一:

char str[]="abcd"与char str[]={'a','b','c','d'}一样吗?char str[]="abcd" 等价于 char str[]={'a','b','c','d','\0'},实际上占用5bytes内存。

char str[]={'a','b','c','d'} 占用4bytes内存。

常识二:

getchar()是stdio.h中的库函数,它的作用是从stdin流中读入一个字符,1.从缓冲区读走一个字符,相当于清除缓冲区2.前面的scanf()在读取输入时会在缓冲区中留下一个字符'\n'(输入完s[i]的值后按回车键所致),所以如果不在此加一个getchar()把这个回车符取走的话,gets()就不会等待从键盘键入字符,而是会直接取走这个“无用的”回车符,从而导致读取有误3.getchar()是在输入缓冲区顺序读入一个字符(包括空格、回车和Tab)getchar()使用不方便,解决方法: (1)使用下面的语句清除回车: while(getchar()!='\n'); (2)用getche()或getch()代替getchar(),其作用是从键盘读入一个字符(不用按回车),注意要包含头文件<conio.h>    

然后是关于DFS的一道例题

问题描述

很久以前,T王国空前繁荣。为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。

为节省经费,T国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。

J是T国重要大臣,他巡查于各大城市之间,体察民情。所以,从一个城市马不停蹄地到另一个城市成了J最常做的事情。他有一个钱袋,用于存放往来城市间的路费。

聪明的J发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关,在走第x千米到第x+1千米这一千米中(x是整数),他花费的路费是x+10这么多。也就是说走1千米花费11,走2千米要花费23。

J大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?

输入格式

输入的第一行包含一个整数n,表示包括首都在内的T王国的城市数

城市从1开始依次编号,1号城市为首都。

接下来n-1行,描述T国的高速路(T国的高速路一定是n-1条)

每行三个整数Pi, Qi, Di,表示城市Pi和城市Qi之间有一条高速路,长度为Di千米。

输出格式

输出一个整数,表示大臣J最多花费的路费是多少。

样例输入1
5
1 2 2
1 3 1
2 4 5
2 5 4
样例输出1
135
输出格式

大臣J从城市4到城市5要花费135的路费。

解题思路:其实这是一道非常简单的搜索类型题目,关键点是如何判断出深搜时最长的一条路?其实,像这种题目,是存在一个知识点的,即这题就是让求:树的直径(树上最长的简单路径即为树的直径。求树的直径的方法就是在树上任选一点u,求距离点u最远的点y,再求距离点y最远的点s,点y到点s的距离即为树的直径。)这样,我们很明确得出:树的直径即为最长路径。

另外我们做题之前还要考虑一个经验性问题,就是这个题目,并没有个数据范围,比如:城市数目,即节点数目。所以我们构建图时,需要设定一个范围非常大的图,所以我们要使用邻接表表示图节点之间的联系

附程序代码及重要注释:

#include <iostream>    
#include <cstring>  
#include<vector>
using namespace std;
const int MAX=10005; 
int visit[MAX];
int len,n,st;
struct node{    //构建与某节点相连的节点及权值
	int v,w;
	node(int vv,int ww){
		v=vv;
		w=ww;
	}
};
vector<node> vt[10005];  //将数组定义为10005,基本就满足“很大”这个条件了
void getValue(int len){
	cout<<(10*len+(len*(1+len)/2));
} 
void dfs(int u,int cur){
	if(len<cur){
		len=cur;
		st=u;
	}
	int l=vt[u].size();
	for(int i=0;i<l;i++){
		int v=vt[u][i].v;
		int w=vt[u][i].w;
		if(!visit[v]){
			visit[v]=1;
			dfs(v,cur+w); 
		}
	}
}

int main () {  
    int x,y,z;
    memset(visit,0,sizeof(visit));
    cin>>n;
    for(int i=0;i<n-1;i++){
    	cin>>x>>y>>z;
    	vt[x].push_back(node(y,z)); //无向图,插入两边节点
    	vt[y].push_back(node(x,z));
	}
	len=0;
	visit[1]=1;
	dfs(1,0);
	len=0;
	memset(visit,0,sizeof(visit));//一定要记得清空节点访问记录
	visit[st]=1;
	dfs(st,0);
	getValue(len);
    return 0;  
}  

当然,对于这种初级的dfs,我们也可以改成栈优化的dfs,这里不做介绍,自己可以尝试