HDOJ.1010 Tempter of the Bone (DFS)
Tempter of the Bone [从零开始DFS(1)]
从零开始DFS
HDOJ.1342 Lotto [从零开始DFS(0)] — DFS思想与框架/双重DFS
HDOJ.1010 Tempter of the Bone [从零开始DFS(1)] —DFS四向搜索/奇偶剪枝
HDOJ(HDU).1015 Safecracker [从零开始DFS(2)] —DFS四向搜索变种
HDOJ(HDU).1016 Prime Ring Problem (DFS) [从零开始DFS(3)] —小结:做DFS题目的关注点
HDOJ(HDU).1035 Robot Motion [从零开始DFS(4)]—DFS题目练习
HDOJ(HDU).1241 Oil Deposits(DFS) [从零开始DFS(5)] —DFS八向搜索/双重for循环遍历
HDOJ(HDU).1258 Sum It Up (DFS) [从零开始DFS(6)] —DFS双重搜索/去重技巧
HDOJ(HDU).1045 Fire Net [从零开始DFS(7)]—DFS练习/check函数的思想
题意分析
给出一张大小为n * m的地图,其中:
X代表墙壁,走不通;
S代表起点;
D代表终点;
. 代表空白区域,即可以走通。
求解是否正好可以在第T步的时候走到终点,并且不能走回头路(即走过的地方不能再走)。
典型的地图搜索问题嘛,首先想到的就是DFS。先回顾一下上次探讨的内容。
传送门:
1.几个名词
DFS的搜索原则
递归
剪枝
递归边界
2.DFS的函数模型
void dfs( 参数 )
{
// 递归边界
// 可以是检查是否满足解的要求
// 完成某系列动作
// 继续递归调用dfs
}
3.其实上次有2个问题没有讨论的太明白
(1)
Q:没有进行排序,为什么自动是字典序?
A:因为题目给的数据,即数组a本来就是按照升序排好的,按照上次说的搜索原则,搜索到的解,一定是按照递增排列好的。每次递归调用dfs的时候,都是先假定选取这个数字,然后再看不选这个数字的情况,那么选取的这个数字,肯定比不选这个数字以后的情况来的小,所以他的下一个解,一定比这个解的字典序要大。如:
| 位置 | 1 | 2 | …… |
|---|---|---|---|
| a数组 | 1 | 2 | …… |
| b数组 | 1 | 待定 | …… |
若现在b的第二个位置选了2
| 位置 | 1 | 2 | …… |
|---|---|---|---|
| a数组 | 1 | 2 | …… |
| b数组 | 1 | 2 | …… |
那么这组解就是12XXXX
若不选2呢?
| 位置 | 1 | 2 | …… |
|---|---|---|---|
| a数组 | 1 | 2 | …… |
| b数组 | 1 | 待定(3、4等都有可能) | …… |
那么这组解就有可能是:
13XXXX 或者 14XXXX 自然字典序就比上面的情况来的大。
(2)
Q: 在递归调用的时候,如何判断这个节点(或者数组的某个元素)已经访问过了呢?
A:因为上次的题是一维数组并且按顺序访问的,就不用检查是否访问过了。但是这个题就需要检查了,一般采用的是visit数组。
先上代码,掰开了揉碎了慢慢来。。
代码总览
/*
Title:HDOJ.1010
Author:pengwill
Date:2017-2-4
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m,t,x1,y1,x2,y2;
int spx[] = {1,0,-1,0};
int spy[] = {0,1,0,-1};
char mp[10][10];
bool visit[10][10];
bool judge = false;
void init()
{
for(int i = 0;i<n;++i){
for(int j = 0;j<m ;++j){
if(mp[i][j] == 'S') {x1 = i;y1 = j;}
else if(mp[i][j] == 'D') {x2 = i;y2 = j;}
else if(mp[i][j] == 'X') visit[i][j] = true;
}
}
}
bool check(int x, int y)
{
if(x<0||x>=n||y<0||y>=m) return false;
else return true;
}
void dfs(int x, int y, int s)
{
if(check(x, y) == false)return;
visit[x][y] = true;
if(x==x2 && y == y2 && s == t){
judge = true;
return;
}
//int temp = t-((abs(x1-x2) + abs(y1-y2)));
//if(temp<0 ||temp %2 ==1) return;
if(((t-s)%2 != (abs(x-x2) + abs(y-y2)) %2)) return;
for(int i = 0;i<4;++i){
if(!visit[x+spx[i]][y+spy[i]]&&!judge){
dfs(x+spx[i],y+spy[i],s+1);
visit[x+spx[i]][y+spy[i]] = false;
}
}
}
int main()
{
// freopen("in.txt","r",stdin);
while(scanf("%d%d%d",&n,&m,&t) &&(n+m+t)){
memset(mp,0,sizeof(mp));
memset(visit,0,sizeof(visit));
for(int i = 0;i<n;++i)
scanf("%s",mp[i]);
init();
judge = false;
dfs(x1,y1,0);
if(judge) printf("YES\n");
else printf("NO\n");
}
return 0;
}
首先是main函数
while(scanf("%d%d%d",&n,&m,&t) &&(n+m+t)){
memset(mp,0,sizeof(mp));
memset(visit,0,sizeof(visit));
for(int i = 0;i<n;++i)
scanf("%s",mp[i]);
init();
judge = false;
dfs(x1,y1,0);
if(judge) printf("YES\n");
else printf("NO\n");
}
按照题意读取nmt三个数据,并且初始化地图数组mp和检查是否访问过的数组visist,之后读入地图的数据,接着是init函数,看看init做了什么。
void init()
{
for(int i = 0;i<n;++i){
for(int j = 0;j<m ;++j){
if(mp[i][j] == 'S') {x1 = i;y1 = j;}
else if(mp[i][j] == 'D') {x2 = i;y2 = j;}
else if(mp[i][j] == 'X') visit[i][j] = true;
}
}
}
原来是找到起点S和终点D的位置,并且把墙所在坐标的visit写为true,代表访问过。不难理解,既然走不通,就把它变成访问过,只要在dfs过程中稍加判断也不会再次访问了。
回到main函数,接着是吧judge改为false,这个是递归边界。题目只要求找出能还是不能,如果我搜索到某一情况,判定可以走到终点,那么明显就没有必要继续搜索下去了。接下来是重头戏,dfs函数。
void dfs(int x, int y, int s)
{
if(check(x, y) == false)return;//判断是否出界
visit[x][y] = true;//改变当前节点为访问过
if(x==x2 && y == y2 && s == t){//判断是否满足题目要求的解
judge = true;
return;
}
if(((t-s)%2 != (abs(x-x2) + abs(y-y2)) %2)) return;//奇偶剪枝
if(t-s-abs(x-x2)-abs(y-y2)<0) return;//步数不够剪枝
for(int i = 0;i<4;++i){//递归调用dfs
if(!visit[x+spx[i]][y+spy[i]]&&!judge){
dfs(x+spx[i],y+spy[i],s+1);
visit[x+spx[i]][y+spy[i]] = false;
}
}
}
首先dfs有3个形式参数,xy代表入口坐标,即搜索入口,s代表走的步数。首先按照check函数,检查当前坐标是否超出地图边界,若超出,则终止递归。
check函数:
bool check(int x, int y)
{
if(x<0||x>=n||y<0||y>=m) return false;
else return true;
}
接着把当前的位置,变为访问过。紧接着就是判断当前坐标是否就是终点坐标并且看步数是否可题目要求的一致,若是的话,judge变为true表示找到解了,以后递归都没必要做了。
然后是奇偶剪枝。
(图2-1)
Tip:只能上下左右走,不能斜着走。
如图2-1所示,由start到end图中最短路径为红色,走了12步。相比于红色路径,蓝色路径大费周折,大家可以数一下,共走了14步。此时步数的偏移量为14-12=2。奇偶剪枝,就是步数的偏移量永远为偶数(可证明),因此可以得出:最短路径步数+某一偶数(即偏移量)=某一可行解歩数。(下面这句是重点)即最短路歩数和某一可行解歩数的奇偶性相同。
回到题目,若已知最短路径歩数为偶数,且在某一情况下剩余的步数为奇数,那么无论如何也是无法恰好到达终点的,因此这种情况就不用算啦,终止递归。当然步数不够,也是走不到终点的。
接着递归调用dfs,这里有新东西啦,visit[x+spx[i]][y+spy[i]]是什么鬼。
看一下开头定义的的:
int spx[] = {1,0,-1,0};
int spy[] = {0,1,0,-1};
不难发现,spx为0时spy为±1,反之成立。也可以想到,x±1和y±1分别对应的就是地图上,上下左右移动一个单位。这里是个小trick,用这样一个的数组来实现上下左右的移动,毕竟一个for循环多好写。
当且仅当下一个位置没有访问过,并且这个时候还没有找到可行解时,递归调用dfs,在调用结束后,记得要把visit[x+spx[i]][y+spy[i]]置为false。原因很简单,不能影响到下一次循环呀:对于下一次循环[x+spx[i]][y+spy[i]]这个位置,可是没有访问过的。(无后效性??请各位指点)。 这样一道题,就ac了。
多做题,加紧训练吧!
HDOJ.1010 Tempter of the Bone (DFS)的更多相关文章
- HDU 1010 Tempter of the Bone --- DFS
HDU 1010 题目大意:给定你起点S,和终点D,X为墙不可走,问你是否能在 T 时刻恰好到达终点D. 参考: 奇偶剪枝 奇偶剪枝简单解释: 在一个只能往X.Y方向走的方格上,从起点到终点的最短步数 ...
- hdoj 1010 Tempter of the Bone【dfs查找能否在规定步数时从起点到达终点】【奇偶剪枝】
Tempter of the Bone Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Othe ...
- hdu.1010.Tempter of the Bone(dfs+奇偶剪枝)
Tempter of the Bone Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Othe ...
- HDU 1010 Tempter of the Bone(DFS+奇偶剪枝)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1010 题目大意: 输入 n m t,生成 n*m 矩阵,矩阵元素由 ‘.’ 'S' 'D' 'X' 四 ...
- hdu - 1010 Tempter of the Bone (dfs+奇偶性剪枝) && hdu-1015 Safecracker(简单搜索)
http://acm.hdu.edu.cn/showproblem.php?pid=1010 这题就是问能不能在t时刻走到门口,不能用bfs的原因大概是可能不一定是最短路路径吧. 但是这题要过除了细心 ...
- (step4.3.1) hdu 1010(Tempter of the Bone——DFS)
题目大意:输入三个整数N,M,T.在接下来的N行.M列会有一系列的字符.其中S表示起点,D表示终点. .表示路 . X表示墙...问狗能有在T秒时到达D.如果能输出YES, 否则输出NO 解题思路:D ...
- HDU 1010 Tempter of the Bone DFS(奇偶剪枝优化)
需要剪枝否则会超时,然后就是基本的深搜了 #include<cstdio> #include<stdio.h> #include<cstdlib> #include ...
- HDU 1010 Tempter of the Bone (DFS+可行性奇偶剪枝)
<题目链接> 题目大意:一个迷宫,给定一个起点和终点,以及一些障碍物,所有的点走过一次后就不能再走(该点会下陷).现在问你,是否能从起点在时间恰好为t的时候走到终点. 解题分析:本题恰好要 ...
- hdu1010 Tempter of the Bone —— dfs+奇偶性剪枝
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1010 Tempter of the Bone Time Limit: 2000/1000 MS (Ja ...
随机推荐
- ansible基础配置使用(一)
test test test
- http知识点 前端
前端必须明白的http知识点 对于http的报文格式就不多细说了,做为前端开发,我们需要知道前后端联调时的请求和响应之间请求头和返回头之间的关系和每个字段中的涵意,静态文件资源在加载时我们所观察到可性 ...
- 6.2 element和elements
为什么这个要单独拿出来说,因为我在很多群里面看见很多人不能区分这个! 因为之前的包有点问题,另外后续还会更换app,因为部分app可能没有符合的案例场景,我需要找到那个场景给大家做个实例..便于大家跟 ...
- MATLAB实现连续周期信号的频谱分析(正余弦波信号举例)
关于MATLAB实现连续信号的频谱分析,以正余弦波信号频谱分析为例分析如下: 1.含有频率f ,2f和3f的正弦波叠加信号,即: 其中,f =500Hz.试采用Matlab仿真软件对该信号进行频谱分析 ...
- TW实习日记:第20-21天
为什么上周五没写呢,因为上周五一直在熟悉业务流程...根本不会写一些复杂的业务代码,因为没有业务流程图!!!在学校的上需求分析和UML建模课的时候,还有软件工程课的时候,想着这都什么鬼啊,听来干嘛,写 ...
- 【outPut_Class 输出类】使用说明
对象:outPut 说明:定义输出结果类的相关操作.此对象的核心是[JSON]类,所以它继承了[JSON]类的所有方法 重要: 输出结果样式为XML时,自带根节点"root".输出 ...
- 看图写树 (Undraw the Trees UVA - 10562)
题目描述: 原题:https://vjudge.net/problem/UVA-10562 题目思路: 递归找结点 //自己的代码测试过了,一直WA,贴上紫书的代码 AC代码 #include< ...
- 单元测试模块unittest使用学习
工作原理: unittest中最核心的四个概念是:test case, test suite, test runner, test fixture. 一个TestCase的实例就是一个测试用例.什么是 ...
- Python3 迭代器,生成器,装饰器
1.迭代器 迭代器有两个基本方法,iter()和next(),next()完成后会引发StopIteration异常 a='abcdef' b=iter(a) #创建迭代器对象 print(type( ...
- mysql 只返回一条数据
问题描述: 需要得到时间最近的一条记录,但是按照时间字段排完序之后,得到的是全部. 解决办法: order by createtime desc //降序:asc:升序 LIMIT 1