Sicily-1153 解题报告

一.原题中文大意。

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

在这样的8x8的棋盘上任选一个起点,然后模仿象棋里面马的移动规则,只能走“日”字。如果在一个起点开始,走完63步之后能够遍历完,除了起点的其余63个点得话,这次马的周游算成功,然后输出这次周游的路径,就是按马经过的顺序将这64个点输出为一行。

二.算法思想及解题用到的主要数据结构

算法思想与1152另外一道5x5的马的周游问题基本相同,用的是回溯算法的思想,从第一步开始,每次枚举下一步可以移动的位置,然后按一定的顺序选择下一个位置,然后在下一个位置同样递归这个步骤,直到遍历棋盘上的所有的点,该次周游成功,输出路径;或者还没有遍历所有的点就无路可走了,该次周游失败,不输出任何信息。

其实就是简单的深度优先搜索,尽快找到一条满足条件的路径就算完成任务了。与深度优先搜索匹配的数据结构是栈,根据栈先进后出的特性,可以很好地模拟路径回溯搜索的过程。所以用到了一个有64个元素的一维数组记录路径,再用一个长度为64的一维数组记录每个节点的状态,还有两个长度为8的一位数组用于成对存储一个节点的8个相邻位置。不过由于这道题数据量较大,如果每一次都同等地遍历8个邻接位置会超时,所以要用启发式剪枝,记忆搜索。对每个位置的邻接位置按邻接位的个数进行排序,邻接位的个数越少的位置优先级越高,先走那个最没前途的点,这样会更快,因为它这么没前途,要从其它点到达它就更难了,所以先走。

三.详细解题思路

(1)   输入一个源点,计算出源点的横坐标和纵坐标,然后在一个8x8的棋盘(二维数组)上进行深度优先搜索。

(2)   初始化路径数组和状态数组,首先把源点添加到路径数组(栈)并把源点的状态标识为已经遍历过。

(3)   进入到启发式的深度优先搜索,先把源点当做搜索的起点。计算起点的可走邻接点,然后将这些邻接点进行排序,遍历邻接点数目最少的点。把该点添加到路径数组(栈)并把该点的状态标识为已经遍历过。

(4)   以这个点作为起点重复第(3)步,直到找到一条满足条件的周游路径。

四.逐步求精算法描述

1. 五个个全局变量数组:

int moveX[8] = {1, 2, 2, 1, -1, -2, -2, -1};

int moveY[8] = {2, 1, -1, -2, -2, -1, 1, 2};

//  以逆时针方向记录下一步可能的8各方向

bool isVisited[64];

//  布尔数组记录每个节点的状态,是否已经遍历过了

int path[64];

//记录具体经过节点的路径

point adjacent[8];

//存储每个节点的可走邻接点

2. 存储节点的数据结构:

//节点有2个属性:节点的横坐标、纵坐标

typedef struct

{

int x;

int y;

}point;

3. 计算并存储当前节点可以走到得邻接节点数组还有数目:

int addNextPosition(point aPoint)

int countNextPosition(point aPoint)

4. 进行启发式搜索时用到的评价函数:

bool compare(point left, point right)

5. 进行启发式深度优先搜索:

void dfs(point startPoint)

五.程序注释清单

  1 //
  2 //  main.cpp
  3 //  Sicily-1153 回溯算法 启发式剪枝
  4 //
  5 //  Created by ashley on 14-10-21.
  6 //  Copyright (c) 2014年 ashley. All rights reserved.
  7 //
  8
  9 #include <iostream>
 10 #include <utility>
 11 #include <algorithm>
 12 using namespace std;
 13
 14 //用两个长度为8的一维数组来记录当前节点到其8个邻接点的坐标变换
 15 int moveX[8] = {1, 2, 2, 1, -1, -2, -2, -1};
 16 int moveY[8] = {2, 1, -1, -2, -2, -1, 1, 2};
 17 //布尔数组记录每个节点的状态,是否已经遍历过了
 18 bool isVisited[64];
 19 //记录具体路径
 20 int path[64];
 21 int counter;
 22 bool success;
 23 //用来存储每个节点的横坐标和纵坐标
 24 typedef struct
 25 {
 26     int x;
 27     int y;
 28 }point;
 29 //存储每个节点的可走邻接点
 30 point adjacent[8];
 31
 32 //计算每个节点的可走邻接点
 33 int addNextPosition(point aPoint)
 34 {
 35     int sum = 0;
 36     for (int i = 0; i < 8; i++) {
 37         point newPoint = {aPoint.x + moveX[i], aPoint.y + moveY[i]};
 38         int number = newPoint.y * 8 + newPoint.x + 1;
 39
 40         if (newPoint.x < 8 && newPoint.x >= 0 && newPoint.y < 8 && newPoint.y >= 0 && isVisited[number - 1] == false) {
 41             adjacent[sum++] = newPoint;
 42         }
 43     }
 44     return sum;
 45 }
 46
 47 //计算一个节点可走邻接点的数目
 48 int countNextPosition(point aPoint)
 49 {
 50     int sum = 0;
 51     for (int i = 0; i < 8; i++) {
 52         point newPoint = {aPoint.x + moveX[i], aPoint.y + moveY[i]};
 53         int number = newPoint.y * 8 + newPoint.x + 1;
 54         if (newPoint.x < 8 && newPoint.x >= 0 && newPoint.y < 8 && newPoint.y >= 0 && isVisited[number - 1] == false) {
 55             sum++;
 56         }
 57     }
 58     return sum;
 59 }
 60
 61 //对邻接点节点按优先级排序的时候用到的比较函数,可走邻接点数越少的节点优先级越高
 62 bool compare(point left, point right)
 63 {
 64     return countNextPosition(left) < countNextPosition(right);
 65 }
 66
 67 //启发式深度优先搜索
 68 void dfs(point startPoint)
 69 {
 70     if (counter == 64) {
 71         success = true;
 72         cout << path[0];
 73         for (int i = 1; i < 64; i++) {
 74             cout << " " << path[i];
 75         }
 76         cout << endl;
 77         return;
 78     }
 79     int key = addNextPosition(startPoint);
 80     //将邻接点排序
 81     sort(adjacent, adjacent + key, compare);
 82     for (int i = 0; i < key; i++) {
 83         point choicePoint = adjacent[i];
 84         int num = choicePoint.y * 8 + choicePoint.x + 1;
 85         isVisited[num - 1] = true;
 86         path[counter] = num;
 87         counter++;
 88         //判断搜索是否成功
 89         if (success) {
 90             return;
 91         }
 92         dfs(choicePoint);
 93         //回溯
 94         isVisited[num - 1] = false;
 95         counter--;
 96     }
 97 }
 98 int main(int argc, const char * argv[])
 99 {
100     int source;
101     while (cin >> source) {
102         if (source == -1) {
103             break;
104         }
105         //计算源点的横坐标和纵坐标
106         point sourcePoint = {(source - 1) % 8, (source - 1) / 8};
107         for (int i = 0; i < 64; i++) {
108             isVisited[i] = false;
109         }
110         success = false;
111         counter = 1;
112         //源点只能走一次
113         isVisited[source - 1] = true;
114         path[counter - 1] = source;
115         dfs(sourcePoint);
116     }
117     return 0;
118 }

六.测试数据(5-10组有梯度的测试数据,要考虑边界条件)

A. 一般数据

(1)10

10 4 14 8 23 40 55 61 51 57 42 25 19 2 17 34 49 59 53 63 48 31 16 6 12 29 46 56 62 52 58 41 35 50 60 45 39 24 7 13 3 9 26 36 30 20 5 15 32 22 28 38 21 11 1 18 33 43 37 47 64 54 44 27

(2)26

26 9 3 13 7 24 14 8 23 40 55 61 51 57 42 59 49 34 17 2 19 4 10 25 35 41 58 52 62 56 39 29 46 63 48 31 16 6 12 18 1 11 5 20 30 15 32 22 28 45 60 50 33 43 53 36 21 38 44 27 37 54 64 47

(3)27

27 10 4 14 8 23 40 55 61 51 57 42 25 19 2 17 34 49 59 44 50 33 18 1 11 5 15 32 38 48 63 53 47 64 54 60 43 58 41 26 9 3 20 37 31 16 6 21 36 46 56 62 52 35 29 12 22 39 24 7 13 30 45 28

(4)38

38 55 40 23 8 14 24 7 13 3 9 26 41 58 52 62 56 39 29 46 63 48 31 16 6 12 2 17 11 1 18 33 50 60 54 64 47 32 15 5 22 28 45 30 20 35 25 10 4 21 27 37 43 53 36 19 34 49 59 44 61 51 57 42

边界数据

(5)25

25 10 4 14 8 23 40 55 61 51 57 42 59 49 34 17 2 19 9 3 13 7 24 30 15 32 47 64 54 48 63 53 38 44 50 60 45 62 56 39 29 46 36 21 31 16 6 12 27 33 18 1 11 28 22 5 20 37 43 26 41 58 52 35

(6)32

32 15 5 11 1 18 3 9 26 41 58 52 62 56 39 24 7 13 30 47 64 54 48 63 53 59 49 43 60 50 33 27 17 2 12 22 16 6 23 8 14 31 37 20 10 4 21 38 28 45 55 40 46 61 44 29 35 25 42 57 51 36 19 34

(7)1

1 18 3 9 26 41 58 52 62 56 39 24 7 13 23 8 14 4 10 25 42 57 51 61 55 40 46 63 48 31 16 6 12 2 19 29 35 45 60 50 33 43 49 59 53 36 30 20 5 15 32 22 28 38 21 11 17 34 44 27 37 54 64 47

(8)8

8 14 24 7 13 3 9 26 41 58 52 62 56 39 54 64 47 32 15 5 22 16 6 23 40 30 20 37 31 48 63 46 61 55 38 53 59 49 43 60 45 28 11 1 18 33 50 35 29 12 2 17 34 44 27 21 4 10 25 19 36 42 57 51

(9)64

64 47 32 15 5 11 1 18 3 9 26 41 58 52 62 56 39 24 7 13 30 40 55 61 46 63 48 54 60 45 51 57 42 59 49 43 53 36 21 38 28 22 16 6 23 8 14 31 37 20 10 4 19 25 35 29 12 2 17 34 44 27 33 50

七.对时间复杂度,空间复杂度方面的分析、估算及程序优化的分析和改进

一般的深度优先的时间复杂度是O(n2)。如果在进行深度优先搜索的时候,不优先搜索子节点较少的节点,会大大增加时间和空间的复杂度,甚至无法通过sicily的测试。所以,要选择启发式的深度优先算法,并设定评价函数,由于邻接点数目越少的节点可以走到终点的可能性越小,所以搜索路径的时候应该优先考虑邻接点少的节点,所以节点的邻接点数目越少优先级越高。这样会大大减少犯错的机会,也就会减少时间复杂度。深度优先算法和启发式深度优先算法的空间复杂度都是O(n),还是可以接受的。

时间: 2024-08-04 08:43:07

Sicily-1153 解题报告的相关文章

Sicily 1151 解题报告(魔板,广搜)

      I.     原题中文大意 魔板由2*4个方块组成,用1到8的数字表示不同的块. 其初始状态是 1 2 3 4 8 7 6 5 对魔板可进行三种基本操作,这三种基本操作,可将任一种状态装换成另一种状态. A (上下行互换) B (行循环右移一格) C (中间四块顺时针转90) 8 7 6 5 1 2 3 4 4 1 2 3 5 8 7 6 1 7 2 4 8 6 3 5 II.       算法思想及解题用到的主要数据结构 广度优先搜索,已搜索过的节点不再进行记录,保证第一个找到的解

Sicily 1090. Highways 解题报告

题目链接:Sicily 1090 思路: 简单的最小生成树问题,这里用prim算法即可.用visited数组记录每个结点是否已经被访问,即是否已经在最小生成树中.每次从不在最小生成树中的结点中取出一个key值最小的结点放入生成树中,key值表示结点到已经在生成树中点集合的最小距离.每次加入一个结点后更新与它相邻的结点的key值. 代码: #include <iostream> #include <queue> #include <stdio.h> #include &l

Sicily 1308. Dependencies among J 解题报告

题目:1308. Dependencies among J 思路: 比较简单的一道题,要知道m最早完成的时间,只需要找出所有需要在m之前完成的工作,将它们的完成时间加起来即可.这里使用vector的数组存储每个结点的邻接点,从结点m开始,依次宽度优先搜索m的每个邻接点...数组visited记录每个结点是否被访问过,遇到已经访问过的结点直接跳过. 读取的时候一开始没有找到解决办法,要读取一行的数字,并且要判断是否换行,可以首先读取第一个数即完成时间,然后用getchar读取一个字符,如果是'\n

Sicily 1350. Piggy banks 解题报告

题目:1350. Piggy banks 思路: 首先把每个钥匙的位置存进key_positions[]中,然后从第一个bank开始,用不同的color给它们分组.比如第一个bank的钥匙在第二个bank中,那么可以直接先开第二个,第二个钥匙在第四个bank中,同样可以先开第四个,以此类推,直到某个钥匙出现在前面的同一组的某个bank中则需要打破一个.visited数组初始化为0,然后访问过后标记为颜色的编号.第21行visited[cur] == color只有找到颜色相同即在同一组的才需要打

解题报告 之 POJ3057 Evacuation

解题报告 之 POJ3057 Evacuation Description Fires can be disastrous, especially when a fire breaks out in a room that is completely filled with people. Rooms usually have a couple of exits and emergency exits, but with everyone rushing out at the same time

hdu 1541 Stars 解题报告

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1541 题目意思:有 N 颗星星,每颗星星都有各自的等级.给出每颗星星的坐标(x, y),它的等级由所有比它低层(或者同层)的或者在它左手边的星星数决定.计算出每个等级(0 ~ n-1)的星星各有多少颗. 我只能说,题目换了一下就不会变通了,泪~~~~ 星星的分布是不是很像树状数组呢~~~没错,就是树状数组题来滴! 按照题目输入,当前星星与后面的星星没有关系.所以只要把 x 之前的横坐标加起来就可以了

【百度之星2014~初赛(第二轮)解题报告】Chess

声明 笔者最近意外的发现 笔者的个人网站http://tiankonguse.com/ 的很多文章被其它网站转载,但是转载时未声明文章来源或参考自 http://tiankonguse.com/ 网站,因此,笔者添加此条声明. 郑重声明:这篇记录<[百度之星2014~初赛(第二轮)解题报告]Chess>转载自 http://tiankonguse.com/ 的这条记录:http://tiankonguse.com/record/record.php?id=667 前言 最近要毕业了,有半年没做

2016 第七届蓝桥杯 c/c++ B组省赛真题及解题报告

2016 第七届蓝桥杯 c/c++ B组省赛真题及解题报告 勘误1:第6题第4个 if最后一个条件粗心写错了,答案应为1580. 条件应为abs(a[3]-a[7])!=1,宝宝心理苦啊.!感谢zzh童鞋的提醒. 勘误2:第7题在推断连通的时候条件写错了,后两个if条件中是应该是<=12 落了一个等于号.正确答案应为116. 1.煤球数目 有一堆煤球.堆成三角棱锥形.详细: 第一层放1个, 第二层3个(排列成三角形), 第三层6个(排列成三角形), 第四层10个(排列成三角形). -. 假设一共

[noip2011]铺地毯(carpet)解题报告

最近在写noip2011的题,备战noip,先给自己加个油! 下面是noip2011的试题和自己的解题报告,希望对大家有帮助,题目1如下 1.铺地毯(carpet.cpp/c/pas) [问题描述]为了准备一个独特的颁奖典礼,组织者在会场的一片矩形区域(可看做是平面直角坐标系的第一象限)铺上一些矩形地毯.一共有n 张地毯,编号从1 到n.现在将这些地毯按照编号从小到大的顺序平行于坐标轴先后铺设,后铺的地毯覆盖在前面已经铺好的地毯之上.地毯铺设完成后,组织者想知道覆盖地面某个点的最上面的那张地毯的

ACdream 1203 - KIDx&#39;s Triangle(解题报告)

KIDx's Triangle Time Limit: 2000/1000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Others) Submit Statistic Next Problem Problem Description One day, KIDx solved a math problem for middle students in seconds! And than he created this problem. N