1153 马的周游问题

1153 马的周游问题

标签: sicily algorithm 递归与回溯



原题中文大意

原题链接

一个有限大小的棋盘上有一只马, 给出初始时马的位置, 找出一条马移动的路线,经过所有格子各一次.


算法思想

  • 深搜
  • 枚举马能走的所有格子, 直到找到一条路径覆盖所有格子
  • 回溯
  • 剪枝 (尽可能地减少搜索空间)

数据结构

用一个结构体来保存马每次走的一个格子的信息

struct nodes {
    /* data */
    int x, y; // the coordinate of some grid
    int idx;  // the index of (x, y), starting from 1
    int countWays; // 从当前格子出发, 下一步可以走的格子数
};

// 比较函数, 用来排序
bool cmp(const nodes a, const nodes b) {
    return a.countWays < b.countWays;
}

解题思路及算法描述

  1. 初始化搜索路径的起点posIdx, 和一个visited数组(bool类型)为false, 及一个记录已经走过的格子的数组nums和其个数的变量len,

  2. 将起点坐标传进search递归函数, 然后进行递归, 具体递归过程如下:
    2.1 将visited[posIdx]设为true, 并记录该格子nums[len++] = posIdx,
    2.2 如果len==所有格子数, 则打印路径, 并返回true,
    2.3 搜索posIdx可走的格子validGrids, 并根据alidGrid.countWaysvalidGrids进行升序排序(这里进行剪枝), 因为从可行格子较少的格子开始搜索, 会减少后续格子的搜索路径,
    2.4 对validGrids的每个格子进心递归(即调用search函数), 并根据其返回值来确定是否结束回溯
    2.5 进心回溯操作 > --lenvisited[posIdx]设为false, 并返回false(这里因为从当前`posIdx出发进行搜索路径失败了),

  3. 进行下一轮递归(即重复步骤1和2), 直到输入结束.


code

// Problem#: 1153
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

using namespace std;    

const int Row = 8;  // x-coor
const int Col = 8;  // y-coor
const int GridNum = Row * Col;

int nums[GridNum + 1];
bool visited[GridNum + 1];

struct nodes {
    /* data */
    int x, y;
    int idx;
    int countWays;
};

bool cmp(const nodes a, const nodes b) {
    return a.countWays < b.countWays;
}

const int ValNum = 8;
const int valXCoor[] = {-2, -2, -1, 1, 2,  2,  1, -1};
const int valYCoor[] = {-1,  1,  2, 2, 1, -1, -2, -2};

int getCountWays(const int posIdx) {
    bool isMode = (posIdx % Col) == 0;
    int x = posIdx / Col;
    x = isMode ? x - 1 : x;
    int y = (posIdx - 1) % Col;

    int count = 0;
    for(int i = 0; i < ValNum; i++) {
        int x2 = x + valXCoor[i];
        int y2 = y + valYCoor[i];
        int idx = x2 * Col + y2 + 1;

        bool flag = (x2 >= 0 && x2 < Row) && (y2 >= 0 && y2 < Col) && !visited[idx];
        if(flag) {
            count++;
        }
    }

    return count;
}

std::vector<nodes> getValidGrids(const int posIdx) {
    bool isMode = (posIdx % Col) == 0;
    int x = posIdx / Col;
    x = isMode ? x - 1 : x;
    int y = (posIdx - 1) % Col;

    std::vector<nodes> validGrids;

    for(int i = 0; i < ValNum; i++) {
        int x2 = x + valXCoor[i];
        int y2 = y + valYCoor[i];
        int idx = x2 * Col + y2 + 1;

        bool flag = (x2 >= 0 && x2 < Row) && (y2 >= 0 && y2 < Col) && !visited[idx];
        if(flag) {
            nodes grid;
            grid.x = x2;
            grid.y = y2;
            grid.idx = idx;
            grid.countWays = getCountWays(idx);

            validGrids.push_back(grid);
        }
    }

    sort(validGrids.begin(), validGrids.end(), cmp);
    return validGrids;
}

void printInfo() {
    int i;
    for(i = 0; i < Row * Col - 1; i++) {
        std::cout << nums[i] << " ";
    }

    std::cout << nums[i] << std::endl;
}

bool search(int posIdx, int& len) {
    visited[posIdx] = true;
    nums[len] = posIdx;
    len++;

    if(len == GridNum) {
        printInfo();
        return true;
    }

    std::vector<nodes> validGrids = getValidGrids(posIdx);
    int size = validGrids.size();

    for(int i = 0; i < size; i++) {
        if(search(validGrids[i].idx, len)) {
            return true;
        }
    }

    len--;
    visited[posIdx] = false;

    return false;

}

int main() {
    int posIdx;

    while(cin >> posIdx && posIdx != -1) {
        // fill(visited, false, GridNum * sizeof(bool));
        // memset(visited, false, GridNum * sizeof(bool));
        for(int i = 0; i <= GridNum; i++) {
            visited[i] = false;
        }

        int len = 0;
        search(posIdx, len);
    }

    return 0;
}                                 

测试数据

下面给出一些测试样例, 其中每个样例的第一行表示其输入, 第二行表示其输出
1. 2

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

  1. 4

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

  2. 5

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

  3. 10

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

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

  5. 20

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

  6. 21

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

  7. 17

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

9 -1

end


时间复杂度分析

总体来说, 对其进行剪枝后, 时间复杂度为O(n), 其中n为格子的总数


end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值