代码地址: https://github.com/laiy/Datastructure-Algorithm/blob/master/sicily/1153.c

题目如下:

1153. 马的周游问题

Constraints

Time Limit: 1 secs, Memory Limit: 32 MB , Special Judge

Description

和题目C同样的任务,这里只是把棋盘扩大到标准的国际象棋。对这样一个8 * 8的棋盘用同样的方法编号如下:

1     2     3       4     5     6       7     8

9     10       11    12       13    14       15    16

17    18       19    20       21    22       23    24

25    26       27    28       29    30       31    32

33    34       35    36       37    38       39    40

41    42       43    44       45    46       47    48

49    50       51    52       53    54       55    56

57    58       59    60       61    62       63    64

Input

输入有若干行。每行一个整数N(1<=N<=64),表示马的起点。最后一行用-1表示结束,不用处理。

Output

对输入的每一个起点,求一条周游线路。对应地输出一行,有64个整数,从起点开始按顺序给出马每次经过的棋盘方格的编号。相邻的数字用一个空格分开。

典型的搜索问题,1152数据规模较小,直接DFS就可以过,然而1153不行,以下为超时代码(>0.99s):

 // Problem#: 1153
// Submission#: 3914599
// The source code is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
// URI: http://creativecommons.org/licenses/by-nc-sa/3.0/
// All Copyright reserved by Informatic Lab of Sun Yat-sen University
#include <cstdio>
#include <cstring> bool visited[][];
int y_direction[] = {-, -, -, -, , , , };
int x_direction[] = {-, , , -, , -, -, };
int record[];
bool found; inline bool is_valid(int x, int y) {
return x >= && x < && y >= && y < ;
} inline int get_number(int x, int y) {
return x * + y + ;
} void dfs(int x, int y, int step) {
if (visited[x][y] || found)
return;
visited[x][y] = true;
record[step] = get_number(x, y);
if (step == ) {
found = true;
return;
}
int temp_x, temp_y;
for (int i = ; i < ; i++) {
temp_x = x + x_direction[i], temp_y = y + y_direction[i];
if (is_valid(temp_x, temp_y) && !visited[temp_x][temp_y])
dfs(temp_x, temp_y, step + );
}
visited[x][y] = false;
} int main() {
int n;
while (scanf("%d", &n) && n != -) {
found = false;
memset(visited, , sizeof(visited));
dfs((n - ) / , (n - ) % , );
for (int i = ; i < ; i++)
printf("%d ", record[i]);
printf("%d\n", record[]);
}
return ;
}

怎么优化?

答案是: 启发式搜索。

启发式搜索其实很简单,就是在DFS的时候正常情况下是按照固定的顺序对树的节点进行访问的(例如从左到右)。

而启发式搜索则是在DFS搜索的时候对树节点的访问加入一个贪心策略,让每次往下搜索的顺序是有策略性的,有启发性的。(这个贪心策略是为了指向最终的终点)。

那么在马周游问题里面怎么在DFS里面加入这个贪心策略使这个搜索更聪明呢?(更快找到终点)

之前Wansdorff在1823年给出了这个启发策略。

Warnsdorff's rule

Warnsdorf's rule is a heuristic for finding a knight's tour. We move the knight so that we always proceed to the square from which the knight will have the fewest onward moves. When calculating the number of onward moves for each candidate square, we do not count moves that revisit any square already visited. It is, of course, possible to have two or more choices for which the number of onward moves is equal; there are various methods for breaking such ties, including one devised by Pohl [14] and another by Squirrel and Cull.[15]

好,尝试一下,在延伸树的支点的时候对每个支点加权排序之后再遍历。

代码如下:

 #include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm> bool visited[][];
int y_direction[] = {-, -, -, -, , , , };
int x_direction[] = {-, , , -, , -, -, };
int record[];
bool found; struct Node {
int x, y, weight;
Node(int x, int y, int weight) {
this->x = x;
this->y = y;
this->weight = weight;
}
bool operator<(const Node &node) const {
return weight < node.weight;
}
}; inline bool is_valid(int x, int y) {
return x >= && x < && y >= && y < ;
} inline int get_number(int x, int y) {
return x * + y + ;
} inline int get_weight(int x, int y) {
int temp_x, temp_y, weight = ;
for (int i = ; i < ; i++) {
temp_x = x + x_direction[i], temp_y = y + y_direction[i];
if (is_valid(temp_x, temp_y) && !visited[temp_x][temp_y])
weight++;
}
return weight;
} void dfs(int x, int y, int step) {
if (visited[x][y] || found)
return;
visited[x][y] = true;
record[step] = get_number(x, y);
if (step == ) {
found = true;
return;
}
int temp_x, temp_y;
std::vector<Node> v;
for (int i = ; i < ; i++) {
temp_x = x + x_direction[i], temp_y = y + y_direction[i];
if (is_valid(temp_x, temp_y) && !visited[temp_x][temp_y])
v.push_back(Node(temp_x, temp_y, get_weight(temp_x, temp_y)));
}
std::sort(v.begin(), v.end());
for (int i = ; (size_t)i < v.size(); i++)
dfs(v[i].x, v[i].y, step + );
visited[x][y] = false;
} int main() {
int n;
while (scanf("%d", &n) && n != -) {
found = false;
memset(visited, , sizeof(visited));
dfs((n - ) / , (n - ) % , );
for (int i = ; i < ; i++)
printf("%d ", record[i]);
printf("%d\n", record[]);
}
return ;
}

运用此策略惊人的达到了0.00s,在性能的提升上我确实没想到能提升这么多。

以下为两个版本代码提交截图:

Sicily1153-马的周游问题:启发式搜索的更多相关文章

  1. sicily 1153. 马的周游问题

    一.题目描述 在一个8 * 8的棋盘中的某个位置有一只马,如果它走29步正好经过除起点外的其他位置各一次,这样一种走法则称马的周游路线,试设计一个算法,从给定的起点出发,找出它的一条周游路线. 为了便 ...

  2. Sicily 1153: 马的周游问题(DFS+剪枝)

    这道题没有找到一条回路,所以不能跟1152一样用数组储存后输出.我采用的方法是DFS加剪枝,直接DFS搜索会超时,优化的方法是在搜索是优先走出度小的路径,比如move1和move2都可以走,但是如走了 ...

  3. Sicily-1153 解题报告

    一.原题中文大意. 1      2       3      4       5      6         7     8 9     10       11    12      13    ...

  4. 【算法】深度优先 马走日 Hamilton routes

    在n*m的棋盘中,马只能走“日” 字.马从位置(x,y)处出发,把棋盘的每一格都走一次,且只走一次.找出所有路径. ××××××××××××× 类似问题: 在半个中国象棋棋盘上,马在左下角(1,1)处 ...

  5. Java输入输出流进阶

    输入输出的内容是文本内容,考虑使用字符流. 输入输出的内容是二进制内容,考虑使用字节流. 凡是能用记事本打开并查看的内容称为文本文件,反之则为二进制文件. package ch15; import j ...

  6. python之新的开始

    Day 1-Morning     终于开通了新的博客(等待审核的过程用着备忘录敲...)~感谢几位大佬们愿意带我一起学习 大家一起加油!(苟富贵,勿相忘!/doge 哈哈哈) 初学python,以下 ...

  7. Sicily 1151: 简单的马周游问题(DFS)

    这道题嘛,直接使用DFS搜索,然后莫名其妙地AC了.后来看了题解,说是move的顺序不同的话可能会导致超时,这时便需要剪枝,真是有趣.原来自己是误打误撞AC了,hhh.题解还有另一种解法是先把一条完整 ...

  8. PGM学习之七 MRF,马尔科夫随机场

    之前自己做实验也用过MRF(Markov Random Filed,马尔科夫随机场),基本原理理解,但是很多细节的地方都不求甚解.恰好趁学习PGM的时间,整理一下在机器视觉与图像分析领域的MRF的相关 ...

  9. 骑士周游问题跳马问题C#实现(附带WPF工程代码)

    骑士周游问题,也叫跳马问题. 问题描述: 将马随机放在国际象棋的8×8棋盘的某个方格中,马按走棋规则进行移动.要求每个方格只进入一次,走遍棋盘上全部64个方格. 代码要求: 1,可以任意选定马在棋盘上 ...

随机推荐

  1. iOS 十六进制的相加取反

    ios中将NSstring字符串转换成char类型 NSString *string = [NSString stringWithFormat:@"5D"]; const char ...

  2. C语言带参数的main函数

    C语言带参数的main函数 #include<stdio.h> int main(int argc,char*argv[]) { int i; ;i<argc;i++) printf ...

  3. Nodejs简单验证码ccap安装

    首先要求: node npm 安装时如果提示npm-gyp失败,可进行如下操作: 确认python版本2.7+ 安装npm install ccap 如果失败,尝试npm install ccap@0 ...

  4. SlidesJS 3.0.4 在手机上遇到的一些问题及解决办法

    SlidesJS 3.0.4 http://slidesjs.com 在手机上遇到的一些问题及解决办法 1.手机上打开有sliderjs的页面后, 切换到别的页面再回来时, sliderjs部分不能滑 ...

  5. centos下配置多个tomcat同时运行

    首先安装好jdk,下载好tomcat,我的是apache-tomcat-7.0.50,不用专门配置CATALINA_2_BASE,CATALINA_2_HOME等环境变量. 把tomcat解压到lin ...

  6. etTimeout与setInterval方法的区别

    etTimeout与setInterval方法的区别 setTimeout()用于设定在指定的时间之后执行对应的函数或代码.,在全局作用域下执行 setTimeout(code,time[,args… ...

  7. JSP 结构

    网络服务器需要一个JSP引擎,也就是一个容器来处理JSP页面. 容器负责截获对JSP页面的请求.本教程使用内嵌JSP容器的Apache来支持JSP开发. JSP容器与Web服务器协同合作,为JSP的正 ...

  8. Meta 的两个 相关属性

    Meta标签中的apple-mobile-web-app-status-bar-style属性及含义: “apple-mobile-web-app-status-bar-style”作用是控制状态栏显 ...

  9. log4j源码阅读

    基于log4j1.2.17的源代码阅读 org.apache.log4j.xml.DOMConfigurator 类是log4j的xml配置文件初始化类 org.apache.log4j.Proper ...

  10. css3 3D盒子效果

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...