代码地址: 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. asp.net命名规范

    以下命名规范是在编程中,可以辅助快速编程的良好方式之一,我一点点的整理出来,以便形成自己的编程规范.还有待完善... 0.产品命名规范: 结构 层次 产品 模块 功能 命名规则 UI(界面层) Web ...

  2. jQuery UI 日期控件--datepicker

    在web开发中,日期的输入经常会遇到.我们会用的解决方法有: 1.自己写css和js,对日期进行控制:----有点浪费精力和时间: 2.用easyui插件中的日期插件来实现: 3.用juqery-ui ...

  3. JavaScript 删除数组重复元素

    unique :function (array){ var n = {}, r = [], len = array.length, val, type; for (var i = 0; i < ...

  4. 使用sprintf打印float并控制小数位数时引起的问题

    最近在做项目中发现一个Bug,直接把进程搞死,查了一下,居然是一个最不起眼的地方导致的,在此记录一下. 先看下面代码 #include <iostream> #include <st ...

  5. 环状DNA序列

    大意: 一个DNA序列是环状的,这意味着有N个碱基的序列有N种表示方法(假设无重复).而这N个序列有一种最小的表示,这个最小表示的意思是这个序列的字典序最小(字典序的意思是在字典中的大小 比如ABC& ...

  6. [lua]原来这才是表驱动的正确表达方式

    曾经写了个很煞笔的脚本模拟switch..case语法形式.[lua]尝试一种Case语法糖 而今实际项目应用中突发,原来这才是正确的表驱动方式表达.如下所贴: function event_do( ...

  7. 新增的output元素 progress元素 meter元素 keygen元素

    结果图 <output>是双标签 name:定义对象的唯一属性 for:定义输出域相关的一个或多个元素. form:定义所属的一个至多个表单. progress和meter一般和JS一起使 ...

  8. 关于手机端CSS Sprite图标定位的一些领悟

    今天在某个群里面闲逛,看见一个童鞋分享了一个携程的移动端的页面.地址这里我也分享下吧:http://m.ctrip.com/html5/在手机端我都很少用雪碧图合并定位图标,用的比较多就是用字体图标来 ...

  9. Python核心编程2第三章课后练习

    1. 标识符.为什么Python 中不需要变量名和变量类型声明? Python中的变量不需要声明,变量的赋值操作既是变量声明和定义的过程.每个变量在内存中创建,都包括变量的标识,名称和数据这些信息.每 ...

  10. 编程框架—Autofac

    Autofac是一款轻量级的IOC框架,性能高. Autofac基本使用步骤: 1.创建容器建造者(Builder): 2.对Builder注册类型. 3.Buildder创建容器(Container ...