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

      又是一个深夜。。。昨天说今天要找一些关于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,这里不做介绍,自己可以尝试



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值