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。先回顾一下上次探讨的内容。

传送门:

HDOJ.1342 Lotto [从零开始DFS(0)]

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)的更多相关文章

  1. HDU 1010 Tempter of the Bone --- DFS

    HDU 1010 题目大意:给定你起点S,和终点D,X为墙不可走,问你是否能在 T 时刻恰好到达终点D. 参考: 奇偶剪枝 奇偶剪枝简单解释: 在一个只能往X.Y方向走的方格上,从起点到终点的最短步数 ...

  2. 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 ...

  3. 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 ...

  4. HDU 1010 Tempter of the Bone(DFS+奇偶剪枝)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1010 题目大意: 输入 n m t,生成 n*m 矩阵,矩阵元素由 ‘.’ 'S' 'D' 'X' 四 ...

  5. hdu - 1010 Tempter of the Bone (dfs+奇偶性剪枝) && hdu-1015 Safecracker(简单搜索)

    http://acm.hdu.edu.cn/showproblem.php?pid=1010 这题就是问能不能在t时刻走到门口,不能用bfs的原因大概是可能不一定是最短路路径吧. 但是这题要过除了细心 ...

  6. (step4.3.1) hdu 1010(Tempter of the Bone——DFS)

    题目大意:输入三个整数N,M,T.在接下来的N行.M列会有一系列的字符.其中S表示起点,D表示终点. .表示路 . X表示墙...问狗能有在T秒时到达D.如果能输出YES, 否则输出NO 解题思路:D ...

  7. HDU 1010 Tempter of the Bone DFS(奇偶剪枝优化)

    需要剪枝否则会超时,然后就是基本的深搜了 #include<cstdio> #include<stdio.h> #include<cstdlib> #include ...

  8. HDU 1010 Tempter of the Bone (DFS+可行性奇偶剪枝)

    <题目链接> 题目大意:一个迷宫,给定一个起点和终点,以及一些障碍物,所有的点走过一次后就不能再走(该点会下陷).现在问你,是否能从起点在时间恰好为t的时候走到终点. 解题分析:本题恰好要 ...

  9. 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 ...

随机推荐

  1. OSG-漫游

    本文转至http://www.cnblogs.com/shapherd/archive/2010/08/10/osg.html 作者写的比较好,再次收藏,希望更多的人可以看到这个文章 互联网是是一个相 ...

  2. VBA基础之Excel VBA 表格的操作(一)

    一.Excel VBA 表格的操作1. Excel表格的指定以及表格属性的设置 Sub main() '把表格B2的值改为"VBA Range和Cells函数" Range(&qu ...

  3. Beta阶段项目展示博客

    Beta阶段项目展示 团队成员的简介 详细见团队简介 角色 姓名 照片 项目经理,策划 游心 策划 王子铭 策划 蔡帜 美工 赵晓宇 美工 王辰昱 开发.架构师 解小锐 开发 陈鑫 开发 李金奇 开发 ...

  4. POJ 2823 (滑动窗口)

    这道题最容易想到的是用朴素的做法,即 每滑动一次,就遍历一次窗口找出最大最小值,这样时间复杂度为O(n*k),由于题目数据比较大,这种做法肯定是超时的. 另外,根据书上的讲解,还可以采用优先队列来求解 ...

  5. JavaScript初探系列之日期对象

    时间对象是一个我们经常要用到的对象,无论是做时间输出.时间判断等操作时都与这个对象离不开.它是一个内置对象——而不是其它对象的属性,允许用户执行各种使用日期和时间的过程. 一   Date 日期对象 ...

  6. java设计模式简介

    设计模式简介: 设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用.设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案.这些解决方案是众多 ...

  7. ajax的一些实用技巧

    1.尽量优先采用ajax获取html文件,然后再操作dom把数据填充到里面 在实际项目中,如果前端开发人员没有把页面给切分开,那么有如下两种办法可供选择:其一是,在各种点击事件中,用js去拼接并在拼接 ...

  8. <Effective C++>读书摘要--Designs and Declarations<二>

    <Item 20> Prefer pass-by-reference-to-const to pass-by-value 1.By default, C++ passes objects ...

  9. C# Winform防止闪频和再次运行

    其实想实现只允许运行一个实例很简单,就是从program的入口函数入手.有两种情况: 第一种,用户运行第二个的时候给一个提示: using System; using System.Collectio ...

  10. VUE01指令

    一.下载Vue2.0的两个版本: 官方网站:http://vuejs.org/ 开发版本:包含完整的警告和调试模式 生产版本:删除了警告,进行了压缩 二.项目结构搭建 这个部分要视频中有详细讲解. 三 ...