POJ2488 深度优先搜索+回溯

POJ2488

题目

  骑士按照下图所示的走法对棋盘进行巡逻,每个格子只允许巡逻一次,且必须巡逻所有格子。给定棋盘的行数p和列数q,输出一条骑士巡逻路径,若不存在这样一条路径,则输出impossible。

图1 骑士的8种走法
  骑士巡逻问题的简化版本,是哈密顿路径问题的特殊形式,但是是线性时间内可以解决的\(^{[1]}\)。

Sample Input

3
1 1
2 3
4 3

Sample Output

Scenario #1:
A1

Scenario #2:
impossible

Scenario #3:
A1B3C1A2B4C2A3B1C3A4B2C4

算法思路

  深度优先搜索(Depth-First Search,DFS)算法,伪代码如下\(^{[2]}\):

图2 DFS算法伪代码
  DFS算法遍历图会建立一个如图3所示的深度优先搜索树,但是从搜索树中可以看出,原始DFS算法不是我们想要的,我们想要的是每个格子只巡逻一次的DFS算法,也就是说我们需要一个深度为棋盘格子数的搜索树(也即没有分叉的树)。那如何改进呢?我们设置一个计数器cnt用来表示树的深度,假如我们访问节点u时,此时DFS不能进行下去了,我们核验深度是否等于棋盘格子数,若等于,则说明我们找到了所求路径,若不等于,说明我们找的这条路径是不对的,这时我们需要将当前节点重置为未搜索的节点,然后回溯到前一节点,并对前一节点的下一个子节点(即下一个可以巡逻的格子)进行DFS。可以参考代码中的注释进行理解。

图3 深度优先搜索树

  1个WA点:若存在多条巡逻路径,题目要求输出字典序优先的路径,lexicographically first path。也就是说,假如A1B1C1和A1C1B1都是允许的路径,则选择A1B1C1,因为B在C前面。其实这一点挺好实现,只需要从左往右扫描“列”即可保证字典序优先。

代码

Result: 372kB, 0ms.

#include <stdio.h>
#include <string.h>
#include <vector>

int n, p, q;
int delta_row[8] = { -1, 1, -2, 2, -2, 2, -1, 1 };//DFS的搜索顺序,按照列从左往右的顺序进行搜索。
int delta_col[8] = { -2, -2, -1, -1, 1, 1, 2, 2 };//即搜索的优先级(-1, -2)=(1, -2)>(-2, -1)=(2, -1)>(-2, 1)=(2, 1)>(-1, 2)=(1, 2),等于的意思是先搜索哪一个不影响字典序
int explored[30][30];//标记是否为已搜索过的节点
int cnt;
struct Node {
    int row, col;
} path[30];//记录下路径

void Init() {
    memset(explored, 0, sizeof(explored));
    cnt = 0;
}

bool Dfs(int row, int col) {
    explored[row][col] = 1;//标记为已搜索的节点
    cnt++;//深度加1
    path[cnt].row = row;
    path[cnt].col = col;
    for (int i = 0; i < 8; i++) {//按照列从左往右的顺序进行搜索
        int row_new = row + delta_row[i], col_new = col + delta_col[i];
        if (row_new >= 1 && row_new <= p && col_new >= 1 && col_new <= q && !explored[row_new][col_new]) {//骑士要去巡逻的新节点(row_new, col_new)在棋盘范围内且未搜索过
            if (Dfs(row_new, col_new))//若找到路径,直接break,一层层跳出递归,若没找到,对下一个优先级的要去巡逻的新节点进行搜索
                break;
        }
    }
    if (cnt == p * q)//若深度为p*q,说明我们找到了路径
        return true;
    else {//若没找到,回溯到前一节点,当前节点置为未搜索的节点,树的深度减1
        explored[row][col] = 0;
        cnt--;
        return false;
    }
}

int main() {
    scanf("%d", &n);
    for (int t = 1; t <= n; t++) {
        scanf("%d %d", &p, &q);
        printf("Scenario #%d:\n", t);
        Init();
        if (Dfs(1, 1)) {
            for (int i = 1; i <= p * q; i++)
                printf("%c%c", 'A' + path[i].col - 1, '1' + path[i].row - 1);
            printf("\n");
        }
        else
            printf("impossible\n");
        if (t != n)
            printf("\n");
    }
    return 0;
}

参考:

[1] Knight‘s tour
[2] 算法设计

原文地址:https://www.cnblogs.com/wtyuan/p/12106652.html

时间: 2024-10-08 16:03:03

POJ2488 深度优先搜索+回溯的相关文章

Leetcode之深度优先搜索&amp;回溯专题-679. 24 点游戏(24 Game)

深度优先搜索的解题详细介绍,点击 你有 4 张写有 1 到 9 数字的牌.你需要判断是否能通过 *,/,+,-,(,) 的运算得到 24. 示例 1: 输入: [4, 1, 8, 7] 输出: True 解释: (8-4) * (7-1) = 24 示例 2: 输入: [1, 2, 1, 2] 输出: False 注意: 除法运算符 / 表示实数除法,而不是整数除法.例如 4 / (1 - 2/3) = 12 . 每个运算符对两个数进行运算.特别是我们不能用 - 作为一元运算符.例如,[1, 1

Leetcode之深度优先搜索&amp;回溯专题-980. 不同路径 III(Unique Paths III)

深度优先搜索的解题详细介绍,点击 在二维网格 grid 上,有 4 种类型的方格: 1 表示起始方格.且只有一个起始方格. 2 表示结束方格,且只有一个结束方格. 0 表示我们可以走过的空方格. -1 表示我们无法跨越的障碍. 返回在四个方向(上.下.左.右)上行走时,从起始方格到结束方格的不同路径的数目,每一个无障碍方格都要通过一次. 示例 1: 输入:[[1,0,0,0],[0,0,0,0],[0,0,2,-1]] 输出:2 解释:我们有以下两条路径: 1. (0,0),(0,1),(0,2

[算法专题] 深度优先搜索&amp;回溯剪枝

1. Palindrome Partitioning https://leetcode.com/problems/palindrome-partitioning/ Given a string s, partition s such that every substring of the partition is a palindrome. Return all possible palindrome partitioning of s. For example, given s = "aab&

回溯 DFS 深度优先搜索[待更新]

首先申明,本文根据微博博友 @JC向北 微博日志 整理得到,本文在这转载已经受作者授权! 1.概念 回溯算法 就是 如果这个节点不满足条件 (比如说已经被访问过了),就回到上一个节点尝试别的路径 也就是说 走到死胡同里边就往回走,直到找到出口. 回溯 是一种 选优搜索 .许多复杂规模较大的问题都可以用 回溯 解决 ,因此回溯法有 "通用解题方法"的美称. 2.思想 若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束.而 若使用回溯法求任一个解时,只要搜

深度优先搜索(DFS)

定义: (维基百科:https://en.wikipedia.org/wiki/Depth-first_search) 深度优先搜索算法(Depth-First-Search),是搜索算法的一种.是沿着树的深度遍历树的节点,尽可能深的搜索树的分支.当节点v的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点.这一过程一直进行到已发现从源节点可达的所有节点为止.如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止(属于盲目搜索). 基

图的遍历之 深度优先搜索和广度优先搜索

本章会先对图的深度优先搜索和广度优先搜索进行介绍,然后再给出C/C++/Java的实现. 目录 1. 深度优先搜索的图文介绍 1.1 深度优先搜索介绍 1.2 深度优先搜索图解 2. 广度优先搜索的图文介绍 2.1 广度优先搜索介绍 2.2 广度优先搜索图解 3. 搜索算法的源码 深度优先搜索的图文介绍 1. 深度优先搜索介绍 图的深度优先搜索(Depth First Search),和树的先序遍历比较类似. 它的思想:假设初始状态是图中所有顶点均未被访问,则从某个顶点v出发,首先访问该顶点,然

“生动”讲解——深度优先搜索与广度优先搜索

深度优先搜索(Depth First Search,DFS) 主要思想:不撞南墙不回头 深度优先遍历的主要思想就是:首先以一个未被访问过的顶点作为起始顶点,沿当前顶点的边走到未访问过的顶点:当没有未访问过的顶点时,则回到上一个顶点,继续试探访问别的顶点,直到所有的顶点都被访问. 沿着某条路径遍历直到末端,然后回溯,再沿着另一条进行同样的遍历,直到所有的顶点都被访问过为止. 图解: 分析: 通过上面的图例可以非常直观的了解深度优先搜索的工作方式.下面来分析一下如何用代码来实现它. 大家都知道,深度

深度优先搜索与全排列

做题过程中我们经常会遇到这样的问题: 输入一个数n,输出1-n的全排列.可能很多人会想到枚举暴力,这里给大家介绍一种算法:深度优先搜索 在这里举个简单的例子 假如有编号为1 .2.3 的3 张扑克牌和编号为l .2 .3 的3 个盒子. 现在需要将这3 张扑克牌分别放到3 个盒子里面,并且每个盒子有且只能放一张扑克牌.那么一共有多少种不同的放法呢? 首先 我们应该设置一个标志数组 book 记录当前数字是否被使用过. 然后用一个数组a 表示盒子并且初始化a[i]=i; 代码如下: #includ

深度优先搜索的图文介绍

1. 深度优先搜索介绍 图的深度优先搜索(Depth First Search),和树的先序遍历比较类似. 它的思想:假设初始状态是图中所有顶点均未被访问,则从某个顶点v出发,首先访问该顶点,然后依次从它的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和v有路径相通的顶点都被访问到. 若此时尚有其他顶点未被访问到,则另选一个未被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止. 显然,深度优先搜索是一个递归的过程. 2. 深度优先搜索图解 2.1 无向图的深度优先搜索