Sicily 1153.马的周游问题 解题报告

For study!

Posted by Winray on February 22, 2016
原题目:
Constraints

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

Description
在一个8 * 8的棋盘中的某个位置有一只马,如果它走64步正好经过除起点外的其他
位置各一次,这样一种走法则称马的周游路线,试设计一个算法,从给定的起点出发,
找出它的一条周游路线。
为了便于表示一个棋盘,我们按照从上到下,从左到右对棋盘的方格编号,如下所示:
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
马的走法是“日”字形路线,例如当马在位置15的时候,它可以到达2、4、7、11、19、
23、26和28。但是规定马是不能跳出棋盘外的,例如从位置1只能到达9和14。
Input
输入有若干行。每行一个整数N(1<=N<=64),表示马的起点。最后一行用-1表示结束,
不用处理。
Output
对输入的每一个起点,求一条周游线路。对应地输出一行,有64个整数,从起点开始按
顺序给出马每次经过的棋盘方格的编号。相邻的数字用一个空格分开。

算法思想及主要数据结构:

  1. 解题思想:采用 dfs 算法(深度优先搜索),考虑到时间复杂度问题,根据当前节点可到的节点数决定访问顺序。

  2. 主要数据结构:结构体保存节点值,vector保存所有有效节点,sort排序挑选下一个访问节点。

详细解题思路:

  1. 算法的选取。宽度优先搜索算法会把所有可能列出,浪费时间,采用深度优先搜索可以很好解决此问题。但是纯粹的DFS算法同样会超时,应适当剪枝。
  2. 剪枝。马的“日”字形走法有多种,有效的决定走哪个方向可以有效减少时间。显然,当当前可走的方向数越少分支会越少,这样就越容易到达目的地,为此,把下一个可能走的节点的可扩展的位置数保存起来,进行排序,优先走位置数少的节点。
  3. 马的移动分析:马必须走“日”字形,最多可走8个位置,把它化成二维数组帮助判断是否下一位置是否可走。
  4. 路径记录:一共走64步,用一维64个元素的数组记录,然后把刚才的二维数组转化为具体值。
  5. 每个当前位置,调用函数判断它下一个可走的位置数,把扩展的位置数以及当前的位置用结构体保存,把这些结构体保存至vector数据结构,然后排序用于选取下一要走的节点。

实现代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

bool visited[8][8]; 
int path[64];  //保存路径的各个节点 
bool isOk;

//可以走的八个方向 
int canMove[8][2] = {
    {-2, 1}, {-2, -1},
    {-1, 2}, {-1, -2},
    {1, 2}, {1, -2},
    {2, 1}, {2, -1}
};

//借助节点来判断每个节点可以往后走的方向数 
struct node{
    int x, y, weight;
    node(int _x, int _y, int _weight) {
        x = _x;
        y = _y;
        weight = _weight;
    }
    bool operator<(const node&n) const {
        return weight < n.weight;
    }
};

/*
 *判断是否合理
 *1、是否走出了方格
 *2、是否访问过该节点 
 */ 
bool isValid(int tx, int ty) {
    if (tx >= 0 && ty >= 0 && tx < 8 && ty < 8
        && !visited[tx][ty])
        return true;
    return false;
}

//获取当前节点能够到达的方向总数 
int getWeight(int x, int y) {
    int weight = 0;
    for (int i = 0; i < 8; i++) {
        int tx = x+canMove[i][0];
        int ty = y+canMove[i][1];
        if (isValid(tx, ty))
            weight++;
    }
    return weight;
}

/*深度搜索直到访问完所有节点为止*/
void dfs(int x, int y, int index) {
    if (isOk) return;
    if (index == 64) {
        isOk = true;
        cout << path[0];
        for (int i = 1; i < 64; i++)
            cout << " " << path[i];
        cout << endl;
    } else {
        vector<node> temp;
        for (int i = 0; i < 8; i++) {
            int tx, ty;
            tx = x+canMove[i][0];
            ty = y+canMove[i][1];
            if (isValid(tx, ty)) {
          temp.push_back(node(tx, ty, getWeight(tx, ty)));
            }
        }

        /*
         *关键点(启发式):
         *  如果当前节点能够访问到下一个节点的数少的优先搜索 
         */ 
        sort(temp.begin(), temp.end());

        for (int i = 0; i < temp.size(); i++) {
            x = temp[i].x;
            y = temp[i].y;
            visited[x][y] = true;
            path[index] = x*8+y+1;
            dfs(x, y, index+1);
            visited[x][y] = false;
        }
    }
}

int main() {
    int t;
    while (cin >> t && t != -1) {
        memset(visited, 0, sizeof(visited));
        path[0] = t;
        isOk = false;
        int x = (t-1)/8;
        int y = (t-1)%8;
        visited[x][y] = true;
        dfs(x, y, 1);
    }

    return 0;
}