代码随想录 -- day30 -- 332.重新安排行程 、51. N皇后、37. 解数独

【代码随想录训练营】【Day30】第七章|回溯算法|332.重新安排行程|51. N皇后|37. 解数独|总结 阅读详情

332.重新安排行程

这道题目有几个难点:

  1. 一个行程中,如果航班处理不好容易变成一个圈,成为死循环
  2. 有多种解法,字母序靠前排在前面,让很多同学望而退步,如何该记录映射关系呢 ?
  3. 使用回溯法(也可以说深搜) 的话,那么终止条件是什么呢?
  4. 搜索的过程中,如何遍历一个机场所对应的所有机场。

1、死循环

 2、记录映射关系

一个机场映射多个机场,机场之间要靠字母序排列,一个机场映射多个机场,可以使用std::unordered_map,如果让多个机场之间再有顺序的话,就是用std::map 或者std::multimap 或者 std::multiset。

这样存放映射关系可以定义为 unordered_map<string, map<string, int>> targets。

定义为:

unordered_map<string, map<string, int>> targets:unordered_map<出发机场, map<到达机场, 航班次数>> targets。

在遍历 unordered_map<出发机场, map<到达机场, 航班次数>> targets的过程中,可以使用"航班次数"这个字段的数字做相应的增减,来标记到达机场是否使用过了

如果“航班次数”大于零,说明目的地还可以飞,如果“航班次数”等于零说明目的地不能飞了,而不用对集合做删除元素或者增加元素的操作。

回溯三部曲:

1、定义递归函数和参数

// unordered_map<出发机场, map<到达机场, 航班次数>> targets
unordered_map<string, map<string, int>> targets;
bool backtracking(int ticketNum, vector<string>& result) {

初始化一下target和result。

for (const vector<string>& vec : tickets) {
    targets[vec[0]][vec[1]]++; // 记录映射关系
}
result.push_back("JFK"); // 起始机场

2、确定终止条件

拿题目中的示例为例,输入: [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]] ,这是有4个航班,那么只要找出一种行程,行程里的机场个数是5就可以了。

所以终止条件:

回溯遍历,遇到的机场个数总和加一就可以。

if (result.size() == ticketNum + 1) {
    return true;
}

3、单层搜索逻辑

for (pair<const string, int>& target : targets[result[result.size() - 1]]) {
    if (target.second > 0 ) { // 记录到达机场是否飞过了
        result.push_back(target.first);
        target.second--;
        if (backtracking(ticketNum, result)) return true;
        result.pop_back();
        target.second++;
    }
}

最终代码

class Solution {
private:
// unordered_map<出发机场, map<到达机场, 航班次数>> targets
unordered_map<string, map<string, int>> targets;
bool backtracking(int ticketNum, vector<string>& result) {
    if (result.size() == ticketNum + 1) {
        return true;
    }
    for (pair<const string, int>& target : targets[result[result.size() - 1]]) {
        if (target.second > 0 ) { // 记录到达机场是否飞过了
            result.push_back(target.first);
            target.second--;
            if (backtracking(ticketNum, result)) return true;
            result.pop_back();
            target.second++;
        }
    }
    return false;
}
public:
    vector<string> findItinerary(vector<vector<string>>& tickets) {
        targets.clear();
        vector<string> result;
        for (const vector<string>& vec : tickets) {
            targets[vec[0]][vec[1]]++; // 记录映射关系
        }
        result.push_back("JFK"); // 起始机场
        backtracking(tickets.size(), result);
        return result;
    }
};

51. N皇后

 如何确定可以放N皇后

1、45度没有棋子

2、135度没有棋子

3.、左右相邻没有棋子

bool isValid(int row, int col, vector<string>& chessboard, int n) {
    // 检查列
    for (int i = 0; i < row; i++) { // 这是一个剪枝
        if (chessboard[i][col] == 'Q') {
            return false;
        }
    }
    // 检查 45度角是否有皇后
    for (int i = row - 1, j = col - 1; i >=0 && j >= 0; i--, j--) {
        if (chessboard[i][j] == 'Q') {
            return false;
        }
    }
    // 检查 135度角是否有皇后
    for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
        if (chessboard[i][j] == 'Q') {
            return false;
        }
    }
    return true;
}

回溯三部曲:

1、确定函数和参数

我依然是定义全局变量二维数组result来记录最终结果。

参数n是棋盘的大小,然后用row来记录当前遍历到棋盘的第几层了。

vector<vector<string>> result;
void backtracking(int n, int row, vector<string>& chessboard) {

2、终止条件

当递归到棋盘最底层(也就是叶子节点)的时候,就可以收集结果并返回了。

if (row == n) {
    result.push_back(chessboard);
    return;
}

3、单层遍历

递归深度就是row控制棋盘的行,每一层里for循环的col控制棋盘的列,一行一列,确定了放置皇后的位置。

每次都是要从新的一行的起始位置开始搜,所以都是从0开始。

for (int col = 0; col < n; col++) {
    if (isValid(row, col, chessboard, n)) { // 验证合法就可以放
        chessboard[row][col] = 'Q'; // 放置皇后
        backtracking(n, row + 1, chessboard);
        chessboard[row][col] = '.'; // 回溯,撤销皇后
    }
}

最终代码:

class Solution {
private:
vector<vector<string>> result;
// n 为输入的棋盘大小
// row 是当前递归到棋盘的第几行了
void backtracking(int n, int row, vector<string>& chessboard) {
    if (row == n) {
        result.push_back(chessboard);
        return;
    }
    for (int col = 0; col < n; col++) {
        if (isValid(row, col, chessboard, n)) { // 验证合法就可以放
            chessboard[row][col] = 'Q'; // 放置皇后
            backtracking(n, row + 1, chessboard);
            chessboard[row][col] = '.'; // 回溯,撤销皇后
        }
    }
}
bool isValid(int row, int col, vector<string>& chessboard, int n) {
    // 检查列
    for (int i = 0; i < row; i++) { // 这是一个剪枝
        if (chessboard[i][col] == 'Q') {
            return false;
        }
    }
    // 检查 45度角是否有皇后
    for (int i = row - 1, j = col - 1; i >=0 && j >= 0; i--, j--) {
        if (chessboard[i][j] == 'Q') {
            return false;
        }
    }
    // 检查 135度角是否有皇后
    for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
        if (chessboard[i][j] == 'Q') {
            return false;
        }
    }
    return true;
}
public:
    vector<vector<string>> solveNQueens(int n) {
        result.clear();
        std::vector<std::string> chessboard(n, std::string(n, '.'));
        backtracking(n, 0, chessboard);
        return result;
    }
};

37. 解数独

 本题中棋盘的每一个位置都要放一个数字(而N皇后是一行只放一个皇后),并检查数字是否合法,解数独的树形结构要比N皇后更宽更深。

回溯三部曲:

1、确定递归函数和参数

bool backtracking(vector<vector<char>>& board)

2、递归终止条件

本题递归不用终止条件,解数独是要遍历整个树形结构寻找可能的叶子节点就立刻返回

3、遍历

一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,一行一列确定下来之后,递归遍历这个位置放9个数字的可能性!

bool backtracking(vector<vector<char>>& board) {
    for (int i = 0; i < board.size(); i++) {        // 遍历行
        for (int j = 0; j < board[0].size(); j++) { // 遍历列
            if (board[i][j] != '.') continue;
            for (char k = '1'; k <= '9'; k++) {     // (i, j) 这个位置放k是否合适
                if (isValid(i, j, k, board)) {
                    board[i][j] = k;                // 放置k
                    if (backtracking(board)) return true; // 如果找到合适一组立刻返回
                    board[i][j] = '.';              // 回溯,撤销k
                }
            }
            return false;                           // 9个数都试完了,都不行,那么就返回false
        }
    }
    return true; // 遍历完没有返回false,说明找到了合适棋盘位置了
}

判断棋盘是否合法

  • 同行是否重复
  • 同列是否重复
  • 9宫格里是否重复
bool isValid(int row, int col, char val, vector<vector<char>>& board) {
    for (int i = 0; i < 9; i++) { // 判断行里是否重复
        if (board[row][i] == val) {
            return false;
        }
    }
    for (int j = 0; j < 9; j++) { // 判断列里是否重复
        if (board[j][col] == val) {
            return false;
        }
    }
    int startRow = (row / 3) * 3;
    int startCol = (col / 3) * 3;
    for (int i = startRow; i < startRow + 3; i++) { // 判断9方格里是否重复
        for (int j = startCol; j < startCol + 3; j++) {
            if (board[i][j] == val ) {
                return false;
            }
        }
    }
    return true;
}
class Solution {
private:
bool backtracking(vector<vector<char>>& board) {
    for (int i = 0; i < board.size(); i++) {        // 遍历行
        for (int j = 0; j < board[0].size(); j++) { // 遍历列
            if (board[i][j] == '.') {
                for (char k = '1'; k <= '9'; k++) {     // (i, j) 这个位置放k是否合适
                    if (isValid(i, j, k, board)) {
                        board[i][j] = k;                // 放置k
                        if (backtracking(board)) return true; // 如果找到合适一组立刻返回
                        board[i][j] = '.';              // 回溯,撤销k
                    }
                }
                return false;  // 9个数都试完了,都不行,那么就返回false
            }
        }
    }
    return true; // 遍历完没有返回false,说明找到了合适棋盘位置了
}
bool isValid(int row, int col, char val, vector<vector<char>>& board) {
    for (int i = 0; i < 9; i++) { // 判断行里是否重复
        if (board[row][i] == val) {
            return false;
        }
    }
    for (int j = 0; j < 9; j++) { // 判断列里是否重复
        if (board[j][col] == val) {
            return false;
        }
    }
    int startRow = (row / 3) * 3;
    int startCol = (col / 3) * 3;
    for (int i = startRow; i < startRow + 3; i++) { // 判断9方格里是否重复
        for (int j = startCol; j < startCol + 3; j++) {
            if (board[i][j] == val ) {
                return false;
            }
        }
    }
    return true;
}
public:
    void solveSudoku(vector<vector<char>>& board) {
        backtracking(board);
    }
};

SD--根据订单创建出库单(相关的函数列表的介绍系列篇(2)) 创建订单样例代码参见:http://blog.csdn.net/CompassButton/archive/2007/05/09/1601712.aspx 可以根据一个订单或者部分订单项来创建出库单,代码如下:* Populating the item data lx_sales_item-ref_doc = l_vbeln. lx_sales_item-ref_item = c_000010 阅读详情

相关推荐

线性模型:AR、MA、ARMA、ARMAX、ARX、ARARMAX、OE、BJ等

线性模型:AR、MA、ARMA、ARMAX、ARX、ARARMAX、OE、BJ等

zephyr_wang的博客 1万+

代码随想录Day30 332.重新安排行程、51.N皇后、37.解数独、总结

332.重新安排行程、51.N皇后、37.解数独、总结

weixin_46620779的博客 1528

金融波动率的“捕手”:GARCH模型,揭秘市场波动的“聚集密码”

摘要: GARCH模型(广义自回归条件异方差)是金融时间序列分析中用于捕捉“波动聚类”现象的核心工具。传统模型(如ARIMA)无法刻画金融数据的时变波动特性,而GARCH通过建模条件方差的动态变化(如$\sigma_t^2 = \omega + \alpha \varepsilon_{t-1}^2 + \beta \sigma_{t-1}^2$),精准预测波动率。Python实战显示,GARCH能有效拟合模拟金融数据的波动聚类,残差无自相关,验证了其优越性。该模型在波动率预测、风险价值(VaR)计算和期权定

u010257409的博客 2055

代码随想录算法训练营Day30|332.重新安排行程、51. N皇后、37. 解数独

在每一个行程中,如何合理处理每一个航班,使它不形成一个圈而成为死循环;如何让字母序靠前排在前面;使用回溯法的话终止条件如何确定;在搜索过程中如何遍历一个机场所对应的所有机场。N皇后是要求在二维矩阵中放置皇后,要求皇后不能同行、同列、同斜线。二维矩阵中矩阵的高就是这棵树的高度,矩阵的宽就是树形结构中每一个节点的宽度。我们用皇后们的约束条件,来回溯搜索这棵树,只要搜索到了树的叶子节点,说明就找到了皇后们的合理位置了。本题与N皇后虽然都是;类似的棋盘问题,但是更为复杂。

m0_68682181的博客 1313

各种AI的调试日记——辅助工作篇

记录使用各种AI(语言大模型)的心得和总结

m0_73056986的博客 1390

阿里巴巴-AI Agent工程师面试题精选:10道高频考题+答案解析(附PDF)

阿里巴巴AIAgent技术面试要点摘要(149字) 阿里巴巴在AIAgent领域布局完整,面试重点考察系统架构、幻觉治理、成本优化等核心能力。候选人需掌握分层架构设计、多Agent协作死锁处理、Token成本优化策略等关键技术。面试强调工程落地能力,包括工具调用容错、记忆系统设计、安全防护等实践要点。评估体系需覆盖效果、效率、成本多维度指标。未来趋势关注垂直领域专业化、多模态融合等方向。建议准备实际项目案例,突出技术深度与业务价值的结合,展现系统思维和工程能力。

ZHHHHH15的博客 615

代码随想录Day30 | 332.重新安排行程 51. N皇后 37. 解数独

Day30

sRiddle的博客 544

ProgLoss+STAL 技术详解:YOLO26 小目标检测增强方案

本文提出了一种基于ProgLoss渐进式损失函数和STAL小目标感知标签分配的YOLO26增强方案。针对小目标检测中特征信息少、正负样本不平衡等难题,STAL通过尺度感知的标签分配策略动态调整候选样本数量和IoU阈值,为小目标分配更多正样本;ProgLoss则根据训练阶段动态调整分类和回归损失的权重,早期侧重定位能力,后期加强分类精度。实验表明,该组合方案显著提升了小目标检测性能,在COCO数据集上小目标AP达到38.2%,比基线提升6.5个百分点。该方法为遥感、监控等场景的小目标检测提供了有效解决方案。

一键难忘的博客 1283

代码随想录【Day 30】| 332.重新安排行程 、51. N皇后 、37. 解数独

代码随想录【Day 30】| 332.重新安排行程 、51. N皇后 、37. 解数独

Pittsman的博客 278

SPI协议详解(图文并茂+超详细)

先说串口 因为之前写过一篇UART,通用串行异步通讯协议,UART的相关资料 因为UART没有时钟信号,无法控制何时发送数据,也无法保证双发按照完全相同的速度接收数据。因此,双方以不同的速度进行数据接收和发送,就会出现问题。 如果要解决这个问题,UART为每个字节添加额外的起始位和停止位,以帮助接收器在数据到达时进行同步; 双方还必须事先就传输速度达成共识(设置相同的波特率,例如每秒9600位)。 传输速率如果有微小差异不是问题,因为接收器会在每个字节的开头重新同步。相应的协议如下图所示; 如果您注意到

一名热爱技术的工程师的博客。分享技术干货,记录美好生活。永远相信美好的事情即将发生。 33万+

游戏客户端开发工程师部分笔试题解析

你可以选择任意连续K个宝石合并成一个新的宝石,新宝石保持在原队列的位置且它的能量值是这K个宝石能量值之和。给定一个幸运值N=0、一堆包含能量值的宝石,你的任务是找到将所有宝石合并成一个宝石的最大幸运值。2.在一个二维网格中,有一个起点S、一个终点E和一个必须经过的中间点M。C++ 的多态性正是通过基类指针或引用调用派生类覆盖的虚函数实现的,这是运行时多态的核心机制。光线追踪带来的抗锯齿效果是其渲染方法的自然结果,而不是一个独立的算法。宝石具体能量值:[10, 7, 2, 6, 5, 11, 8];

博客 3028

代码随想录算法训练营第day30|332.重新安排行程 、 51. N皇后 、37. 解数独

一个机场映射多个机场,机场之间要靠字母序排列,一个机场映射多个机场,可以使用std::unordered_map,如果让多个机场之间再有顺序的话,就是用std::map 或者std::multimap 或者 std::multiset。递归深度就是row控制棋盘的行,每一层里for循环的col控制棋盘的列,一行一列,确定了放置皇后的位置。从图中,可以看出,二维矩阵中矩阵的高就是这棵树的高度,矩阵的宽就是树形结构中每一个节点的宽度。遍历multiset的时候,不能删除元素,一旦删除元素,迭代器就失效了。

qq_60513199的博客 792

财务数字化——解读美的战略规划之财务组织体系建设【附全文阅读】

本文提出适用于集团企业的财务管控方案,面向财务负责人、HR及战略规划人员。方案整合四种管控模式,结合500强案例,构建"职能定位-架构设计-权责划分-人才发展"体系,涵盖财务五大模块。通过明确总部与事业部权责边界,提供组织架构等实操框架,旨在平衡财务管控与业务支持,提升效率与风控能力,满足集团财务体系规范化建设需求。

cdfunlove的博客 663

代码随想录算法训练营DAY30|332.重新安排行程、51. N皇后、37. 解数独、总结

set()和used数组去重的区别使用set去重的版本相对于used数组的版本效率都要低很多,主要是因为程序运行的时候对unordered_set 频繁的insert,unordered_set需要做哈希映射(也就是把key通过hash function映射为唯一的哈希值)相对费时间,而且insert的时候其底层的符号表也要做相应的扩充,也是费时的。而使用used数组在时间复杂度上几乎没有额外负担!使用set去重,不仅时间复杂度高了,空间复杂度也高了,在本周小结!

Greeneril_的博客 1675

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

代码随想录训练营Day30| 332.重新安排行程 51. N皇后 37. 解数独 总结

problems/回溯总结.md · programmercarl/leetcode-master(代码随想录出品) - Gitee.com。332. 重新安排行程 - 力扣(LeetCode)51. N 皇后 - 力扣(LeetCode)37. 解数独 - 力扣(LeetCode)332.重新安排行程。332.重新安排行程。

weixin_44174445的博客 180

代码随想录刷题Day30 | 332.重新安排行程 | 51. N皇后 | 37. 解数独

代码随想录刷题Day30 | 332.重新安排行程 | 51. N皇后 | 37. 解数独

DUM1615的博客 333

代码随想录算法训练营day30 | 332. 重新安排行程,51. N 皇后,37. 解数独

本节都是难度较高的回溯题,只要准确分辨控制树的深度和宽度的因素即可正确解题。

2301_77450803的博客 572

代码随想录算法训练营day30|LeetCode 332. 重新安排行程、LeetCode 51 N 皇后、LeetCode 37 解数独

代码随想录算法训练营day30

qq_51588716的博客 294

零基础代码随想录【Day30】|| 332.重新安排行程,51. N皇后,37. 解数独,回溯算法总结

给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。一个数独的解法需遵循如下规则: 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

Andrew______的博客 480

代码随想录算法训练营Day30——332重新安排行程、51N皇后、37解数独

【代码】代码随想录算法训练营Day30——332重新安排行程、51N皇后、37解数独。

weixin_45075561的博客 230

代码随想录算法训练营Day30 | 332.重新安排行程 51. N皇后 37. 解数独 总结

回溯解决的问题:组合问题:N个数里面按一定规则找出k个数的集合排列问题:N个数按一定规则全排列,有几种排列方式切割问题:一个字符串按一定规则有几种切割方式子集问题:一个N个数的集合里有多少符合条件的子集棋盘问题:N皇后,解数独等等。

qq_24817679的博客 367

代码随想录算法训练营 | day30 回溯总结,332.重新安排行程,51.N皇后,37.解数独

本题其实可以算是一道hard的题目了,关于本题的难点我在文中已经列出了。如果单纯的回溯搜索(深搜)并不难,难还难在容器的选择和使用上。本题其实是一道深度优先搜索的题目,但是我完全使用回溯法的思路来讲解这道题题目,算是给大家拓展一下思维方式,其实深搜和回溯也是分不开的,毕竟最终都是用递归。代码随想录 (programmercarl.com)回溯专题汇聚为一张图:这个图是莫非毛,所画,总结的非常好,分享给大家。

weixin_45011378的博客 260

代码随想录 Day30 332.重新安排行程 51. N皇后 37. 解数独

如果说该层是可以的但是向下递归的时候下一层没有一个是可以的话,这个时候就会return false;2. 开始的时候就设置两个for循环,并没有像以前一样设置终止条件,也是因为只要是有一种符合的情况的话就可以,所以说设置为bool。2. 注意比较的时候是对于一个char类型的元素进行比较而不是一个int进行比较,需要注意输入以及输出的类型,还有就是加’‘。1. (i/3)*3和(i/3)*3+3是一个小的九宫格的范围,小的点不要忘记*3。思路:本题的思路是很有难度的,跟着卡哥的思想写确实就简单了很多。

weixin_61057535的博客 454

代码随想录Day30-332.重新安排行程,51. N皇后,37. 解数独

【代码】代码随想录Day30-332.重新安排行程,51. N皇后,37. 解数独。

lizhiqiang502的博客 94

代码随想录算法训练营day30 | 332.重新安排行程 51. N皇后 37. 解数独

回溯结束

baimaozi_007的博客 259

代码随想录算法训练营 Day 30 | 332.重新安排行程,51.N皇后,37.解数独

代码随想录-回溯算法总结。

MisakaMirai的博客 100

代码随想录算法训练营Day30 | 332. 重新安排行程 | 51. N皇后 | 37. 解数独 | 总结

总结篇组合问题:N个数里面按一定规则找出k个数的集合排列问题:N个数按一定规则全排列,有几种排列方式切割问题:一个字符串按一定规则有几种切割方式(切割线的组合)子集问题:一个N个数的集合里有多少符合条件的子集棋盘问题:N皇后,解数独等等。

Kolbe_Huang的博客 345
上一篇: 代码随想录 -- day29 -- 491.递增子序列、46.全排列、47.全排列 II
下一篇: 代码随想录 -- day31 -- 455.分发饼干 、376. 摆动序列 、53. 最大子序和
七人酒
博客等级 码龄4年 51粉丝 65原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值