代码地址: 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. SQL学习:查询的用法(1)

    在SQL servre的使用中,查询的用法是最多的.最重要的,也是最难学习的,因此掌握查询的用法很重要. 先将表的示例上图 员工表: 部门表:                             ...

  2. doj常用包

    dojo.raise               抛出一个异常 dojo.errorToString将异常转换为字符串 dojo.render      系统环境对象 dojo.hostenv. ...

  3. 一段C++代码想到的问题

    今天在学习<Unix环境高级编程>,第七章进程环境给出了一个进程的内存分布示意图,从下往上依次为“正文段->初始化数据->未初始化数据(默认初始化为0)->堆(从低地址到 ...

  4. 理解MySQL——索引与优化(转)

    写 在前面:索引对查询的速度有着至关重要的影响,理解索引也是进行数据库性能调优的起点.考虑如下情况,假设数据库中一个表有10^6条记录,DBMS的页 面大小为4K,并存储100条记录.如果没有索引,查 ...

  5. angular 实例笔记之嵌套指令间的传参

    最近在项目中遇到了需要嵌套指令的情况,指令在嵌套后子指令必须获得父指令中的数据来进行判断,但是在写传参的时候遇到了坑,因此记录下来,防止以后遗忘,个人的肤浅理解,欢迎大家留言讨论 首先,关于direc ...

  6. 疯狂的表单-html5新增表单元素和属性

    疯狂的表单 2015/11/27 16:44:07 从三方面来介绍html5表单的新特性 表单结构更灵活 要提交数据的控件可以布局在form标签之外,看下面的代码,表单元素可以写到form元素之外,只 ...

  7. php和js根据子网掩码和ip计算子网

    php $ip = '192.168.6.1'; $mask = '255.255.2.0'; $sub_net = array();//子网 $ip_explode = explode('.', $ ...

  8. 如何在eclips下将一段代码抽取为方法Extract Method

    最近读了读关于重构的文章,做了个小总结(在编程思想目录下<从文章"避免复制与粘贴"到文章"Extract Method"的反思 系列>). 然后因为 ...

  9. uboot启动linux的过程

    一.概述 linux内核镜像常见到的有两种形式,zImage和uImage.这两种文件的格式稍有差别,所以启动这两种格式的内核镜像也会有所不同.目前,uboot只支持启动uImage类型的镜像,对zI ...

  10. 【原】K3Cloud平台开发之Python插件

    有时候我们的表单可能很简单,只是一个简单交互的表单,但有可能还是要劳师动众的给它建个工程写个插件,是不是很不爽?例如我有如下一个表单: 功能很简单就是选个业务对象,收集绑定几个字段名,然后确定返回一个 ...