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. hdu2066一个人的旅行(floyd优化)

    一个人的旅行 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Subm ...

  2. git push origin master 错误解决办法

    一.错误代码如下: error: failed to push some refs to 'https://github.com/wbingithub/drag.git' 二.在网上搜了一下,如下写就 ...

  3. 【20180808模拟测试】T2 k-斐波那契

    描述 k-斐波拉契数列是这样的 f(0)=k;f(1)=k;f(n)=(f(n-1)+f(n-2))%P(n>=2); 现在我们已经知道了f(n)=1,和P: k的范围是[1,P); 求k的所有 ...

  4. 一个简单的页面弹窗插件 jquery.pageMsgFrame.js

    页面弹窗是网站中常用的交互效果,它可以强提示网站的某些信息给用户,或者作用于某些信息的修改等等功能. 这几天在做一个项目的时候,就顺捎把这个插件写一下,栽棵树,自己乘凉吧. 原创博文,转载请注明出处: ...

  5. Mount qcow2 image

    1.Mount a qcow2 image qemu-nbd - QEMU Disk Network Block Device Server: Export QEMU disk image using ...

  6. github 使用“git commit -m"命令时候出现的一个小问题

    git commit -m 使用问题 今天提交文件到github,步骤是: git add abc.py (abc.py是我当前随意写的一个文件名) git commit -m 'add codes ...

  7. Kali信息收集-DNS

    1.whois查询 直接在终端输入whois 域名 2.查找dns服务器 (1)host (2)dig (3)nslookup 3.域传输 4.域名枚举 (1)dnsdict6 kali没有集成这款工 ...

  8. ZOJ 2760 How Many Shortest Path(最短路径+最大流)

    Description Given a weighted directed graph, we define the shortest path as the path who has the sma ...

  9. Sail

    DescriptionThe polar bears are going fishing. They plan to sail from (sx,?sy) to (ex,?ey). However, ...

  10. java报错:Exception in thread "main" java.lang.NoSuchFieldError: INSTANCE

    Exception in thread "main" java.lang.NoSuchFieldError: INSTANCE at org.apache.http.conn.ss ...