Day30 回溯 LeedCode 332.重新安排行程 51. N皇后 37. 解数独 蓝桥杯 与或异或

332. 重新安排行程

给你一份航线列表 tickets ,其中 tickets[i] = [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。

所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。

  • 例如,行程 ["JFK", "LGA"] 与 ["JFK", "LGB"] 相比就更小,排序更靠前。

假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。

示例 1:

输入:tickets = [["MUC","LHR"],["JFK","MUC"],["SFO","SJC"],["LHR","SFO"]]
输出:["JFK","MUC","LHR","SFO","SJC"]

本题用回溯法,遍历所有的票的使用顺序,由于所有机票至少存在一种合理的行程,我们先将票按照起始位置的开头字母小的排序,递归过程中找到其中一种合理行程就返回,该行程一定是字典排序更小的行程。

递归三部曲:

1.确定返回值和参数的类型

使用全局变量保存结果 ,参数传入所有的票(List<List<String>> tickets),和每张票的使用情况(used[])

由于只取第一个结果,所有返回值类型设置为boolean类型,当找到第一个结果时,就返回true

 List<String> path=new LinkedList<>();
    List<String> result;
boolean travel(List<List<String>> tickets,boolean used[])

2.确定递归结束条件

当记录路径的数组的长度==票的长度+1,说明合理用完了所有票,找到了合理旅游路径,结束递归

   if(path.size()==tickets.size()+1){
            result=new ArrayList<>(path);
            return true;
        }

3.单层递归逻辑

递归找到所有方案

 for(int i=0;i<tickets.size();i++){
            if(!used[i]&&path.get(path.size()-1).equals(tickets.get(i).get(0))){
              path.add(tickets.get(i).get(1));
              used[i]=true;
              //找到第一个方案,结束递归
            if( travel(tickets,used))return true;
              path.remove(path.size()-1);
              used[i]=false;
            }

        }
        return false;

总体代码:

class Solution {
    List<String> path=new LinkedList<>();
    List<String> result;
    public List<String> findItinerary(List<List<String>> tickets) {
       boolean[] used=new boolean[tickets.size()];
       //给票排序
       Collections.sort(tickets,(a,b)->a.get(1).compareTo(b.get(1)));
       path.add("JFK");
       travel(tickets,used);
       return result;
    }
    boolean travel(List<List<String>> tickets,boolean used[]){
        if(path.size()==tickets.size()+1){
            result=new ArrayList<>(path);
            return true;
        }
        for(int i=0;i<tickets.size();i++){
            if(!used[i]&&path.get(path.size()-1).equals(tickets.get(i).get(0))){
              path.add(tickets.get(i).get(1));
              used[i]=true;
              //找到第一个方案,结束递归
            if( travel(tickets,used))return true;
              path.remove(path.size()-1);
              used[i]=false;
            }

        }
        return false;
    }
}

使用本方法因为排序的原因会出现超时 

改进方法:

用Map<出发机场, Map<到达机场, 航班次数>> map来记录车票,Map<到达机场, 航班次数>为升序TreeMap

class Solution {
    private Deque<String> res;
    private Map<String, Map<String, Integer>> map;

    private boolean backTracking(int ticketNum){
        if(res.size() == ticketNum + 1){
            return true;
        }
        String last = res.getLast();
        if(map.containsKey(last)){//防止出现null
            for(Map.Entry<String, Integer> target : map.get(last).entrySet()){
                int count = target.getValue();
                if(count > 0){
                    res.add(target.getKey());
                    target.setValue(count - 1);
                    if(backTracking(ticketNum)) return true;
                    res.removeLast();
                    target.setValue(count);
                }
            }
        }
        return false;
    }

    public List<String> findItinerary(List<List<String>> tickets) {
        map = new HashMap<String, Map<String, Integer>>();
        res = new LinkedList<>();
        for(List<String> t : tickets){
            Map<String, Integer> temp;
            if(map.containsKey(t.get(0))){
                temp = map.get(t.get(0));
                temp.put(t.get(1), temp.getOrDefault(t.get(1), 0) + 1);
            }else{
                temp = new TreeMap<>();//升序Map
                temp.put(t.get(1), 1);
            }
            map.put(t.get(0), temp);

        }
        res.add("JFK");
        backTracking(tickets.size());
        return new ArrayList<>(res);
    }
}

本题难在容器的选择


51. N 皇后 - 力扣(LeetCode)

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

Example 1:

Input: n = 4
Output: [[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释:如上图所示,4 皇后问题存在两个不同的解法。

皇后们的约束条件:

  1. 不能同行
  2. 不能同列
  3. 不能同斜线
 Boolean isVaild(char[][] chessboard,int row,int col,int n){
        //把皇后放在n*n的棋盘的(row,col)位置
        //检查列
        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-1;i--,j++){
       if(chessboard[i][j]=='Q')
            return false;
        }
     return true;
    }

 思路:用回溯法,遍历出所有可以放置的可能性

递归三部曲:

1.确定返回值和参数类型

要把所有能摆放的位置都得出,用全局变量public List<List<String>> result=new ArrayList<>();记录所有合理的棋盘,返回值为void,传入棋盘(char[][] chessboard),要放置的皇后在哪一行(int row),棋盘的宽度(n);

 public void backTracking(int n,int row,char[][] chessboard)

2.确定递归结束条件

当row==n时,说明所有行都放置了皇后,找到了一种合理放法,将该棋盘存入result中,递归结束

 if(row==n){
           List<String> temp= array2List(chessboard);
           result.add(temp);
           return;
       }

3.确定单层递归逻辑

遍历该行的每一个位置并检查其合理性,如果合理,进入下一行棋盘的摆放

for(int i=0;i<n;i++){
        //如果当前位置合法,就递归放下一行
        if(isVaild(chessboard,row,i,n)){
        chessboard[row][i]='Q';
        backTracking(n,row+1,chessboard);
        chessboard[row][i]='.';
        }
       }

总体代码:

class Solution {
    public List<List<String>> result=new ArrayList<>();
    public List<List<String>> solveNQueens(int n) {
       char[][] chessboard = new char[n][n];
       for(char[] c:chessboard){
        Arrays.fill(c,'.');
       }
       backTracking(n,0,chessboard);
       return result;
    }
    public void backTracking(int n,int row,char[][] chessboard){
       if(row==n){
           List<String> temp= array2List(chessboard);
           result.add(temp);
           return;
       }
       for(int i=0;i<n;i++){
        //如果当前位置合法,就递归放下一行
        if(isVaild(chessboard,row,i,n)){
        chessboard[row][i]='Q';
        backTracking(n,row+1,chessboard);
        chessboard[row][i]='.';
        }
       }

    }
    //将数组转为list
    List<String> array2List(char[][] chessboard){
        List<String> result=new ArrayList<>();
        for(int i=0;i<chessboard.length;i++){
             StringBuilder temp=new StringBuilder();
        for(int j=0;j<chessboard[0].length;j++){
           temp.append(chessboard[i][j]);
        }
        result.add(temp.toString());
        }
        return result;
    }

    Boolean isVaild(char[][] chessboard,int row,int col,int n){
        //把皇后放在n*n的棋盘的(row,col)位置
        //检查列
        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-1;i--,j++){
       if(chessboard[i][j]=='Q')
            return false;
        }
     return true;
    }
}

37. 解数独

编写一个程序,通过填充空格来解决数独问题。

数独的解法需 遵循如下规则

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)

数独部分空格内已填入了数字,空白格用 '.' 表示。

示例 1:

输入:board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
输出:[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:
 

思路:n皇后问题一行只需要放一个皇后,本题一行可能填好几个数,所以本题是二维递归 ,

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

判断棋盘是否合法

判断棋盘是否合法有如下三个维度:

  • 同行是否重复
  • 同列是否重复
  • 9宫格里是否重复
  Boolean isVaild(int row,int col,char val,char[][] board){
        //同行是否重复
        for(int i=0;i<9;i++){
            if(board[row][i]==val){
                return false;
            }
        }
        //同列是否重复
        for(int i=0;i<9;i++){
            if(board[i][col]==val){
                return false;
            }
        }
        //9宫格内是否重复
        int startRow=row/3*3;
        int startCol=col/3*3;
        for(int i=startRow;i<startRow+3;i++){
            for(int j=startCol;j<startCol+3;j++){
        if(board[i][j]==val){
            return false;
              }
            }
        }
      return true;
    }

 递归三部曲:

1.确定返回值和参数类型

只有一个解,只需要找一个解,返回类型为boolean,当回溯返回true时,结束当前递归

 Boolean backTracking(char[][] board)

当有多个解时,返回类型为void,需要找遍所有可能

传入棋盘char[][] board

2.确定递归结束条件

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

递归的下一层的棋盘一定比上一层的棋盘多一个数,等数填满了棋盘自然就终止(填满当然好了,说明找到结果了),所以不需要终止条件!

3.确定当层逻辑

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

class Solution {
    public void solveSudoku(char[][] board) {
      backTracking(board);
    }
    Boolean backTracking(char[][] board){
         for (int i = 0; i < 9; i++){ // 遍历行
            for (int j = 0; j < 9; j++){ // 遍历列
                if (board[i][j] != '.'){ // 跳过原始数字
                    continue;
                }
                //找到没填数字的空位,在这个空位填1--9
                for(char k='1';k<='9';k++){
                    if(isVaild(i,j,k,board)){
                        board[i][j]=k;
                    if(backTracking(board))return true;
                    board[i][j]='.';//
                    }
                   
                }
                return false;//9个数都填了,都不对,返回false
            }

         }
            //遍历完没有返回false,说明找到了合适棋盘位置了
                return true;
            
    }


    Boolean isVaild(int row,int col,char val,char[][] board){
        //同行是否重复
        for(int i=0;i<9;i++){
            if(board[row][i]==val){
                return false;
            }
        }
        //同列是否重复
        for(int i=0;i<9;i++){
            if(board[i][col]==val){
                return false;
            }
        }
        //9宫格内是否重复
        int startRow=row/3*3;
        int startCol=col/3*3;
        for(int i=startRow;i<startRow+3;i++){
            for(int j=startCol;j<startCol+3;j++){
        if(board[i][j]==val){
            return false;
              }
            }
        }
      return true;
    }
}

思路:本题与上一题思路一致, 一个for循环遍历数组的行,一个for循环遍历数组的列,一行一列确定下来之后,遍历所有符号得出这个位置的数!

代码参考:

import com.sun.org.apache.bcel.internal.generic.NEW;


import java.util.*;

public class Main {
    static int[][] result = new int[5][5];
    static int sum = 0;
    static char[] f = {'&', '|', '^'};

    public static void main(String args[]) {
        int[] A = {1, 0, 1, 0, 1};
        for (int i = 1; i < result.length; i++)
            Arrays.fill(result[i], -1);
        result[0] = A;

        backTracking(result);
        System.out.println(sum);
    }

    public static void backTracking(int[][] result) {
        if (result[4][0] != -1) {
            if (result[4][0] == 1)
                sum++;
            result[4][0] = -1;
            return;
        }

        for (int k = 1; k < 5; k++) {
            for (int m = 0; m < 5 - k; m++) {
 //找到没填数字的空位
                if (result[k][m] != -1)
                    continue;
//遍历所有符号,填上对应的数
                for (int n = 0; n < f.length; n++) {
                    result[k][m] = fun(f[n], result[k - 1][m], result[k - 1][m + 1]);
                    backTracking(result);
                    result[k][m] = -1;
                }
                return;//遍历完当前所有符号,
//表示这个位置填某一个符合的计算结果情况已经遍历完了,
//返回上一层,将上一个数修改
            }
        }
    }

    public static int fun(char a, int b, int c) {
        if (a == '&') {
            return b & c;
        } else if (a == '|') {
            return b | c;
        } else
            return b ^ c;

    }


}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/530993.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【小程序】常用方法、知识点汇总1

欢迎来到《小5讲堂》 这是《小程序》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言请求超时Markdown解析逐行显示效果文本变动事件转发…

C语言—每日选择题—Day65

前言 我们的刷题专栏又又又开始了&#xff0c;本专栏总结了作者做题过程中的好题和易错题。每道题都会有相应解析和配图&#xff0c;一方面可以使作者加深理解&#xff0c;一方面可以给大家提供思路&#xff0c;希望大家多多支持哦&#xff5e; 第一题 1、如下代码输出的是什么…

LINUX系统触摸工业显示器芯片应用方案--Model4(简称M4芯片)

背景介绍&#xff1a; 触摸工业显示器传统的还是以WINDOWS为主&#xff0c;但近年来&#xff0c;安卓紧随其后&#xff0c;但一直市场应用情况不够理想&#xff0c;反而是LINUX系统的触摸工业显示器大受追捧呢&#xff1f; 触摸工业显示器传统是以Windows系统为主&#xff0c…

无线游戏手柄的测试(Windows11系统手柄调试方法)

实物 1、把游戏手柄的无线接收器插入到电脑usb接口中 2、【控制面板】----【查看设备和打印机】 3、【蓝牙和其它设备】--【更多设备和打印机设置】 4、鼠标右键【游戏控制器设置】 5、【属性】 6、【测试】&#xff08;每个按键是否正常&#xff09; 7、【校准】&#xff08;…

学习笔记:解决拖延

1 解决拖延&#xff0c;减轻压力的关键心态和方法 1.1 要点梳理 拖延是因为自己一直在逃避&#xff0c;重点是要有效突破逃避圈&#xff0c;进入学习圈&#xff0c;扩展成长圈。 毒蛇曲线&#xff08;见思维导图&#xff09;中越是临近截止期限&#xff0c;拖延的焦虑越上升…

【项目实战经验】DataKit迁移MySQL到openGauss(下)

上一篇我们分享了安装、设置、链接、启动等步骤&#xff0c;本篇我们将继续分享迁移、启动~ 目录 9. 离线迁移 9.1. 迁移插件安装 中断安装&#xff0c;比如 kill 掉java进程&#xff08;安装失败也要等待300s&#xff09; 下载安装包准备上传 缺少mysqlclient lib包 mysq…

树的基本概念(重点)

1.一下概念很重要 以下面的的树为例&#xff1a; 1.节点的度&#xff1a; 一个节点所含有的子树的个数就是这个节点的度&#xff0c;注意一个子节点也算一个子树。 如上图&#xff1a; B节点的度1&#xff1b; A节点的度6&#xff1b; 2.树的度&#xff1a; 一个树的度这…

实模式和保护模式

前言 大家好&#xff0c;我是jiantaoyab&#xff0c;内存中的每一个字节都有一个唯一的地址&#xff0c;通过这个地址我们能去取出一个个比特&#xff0c;我们可以称这个过程为寻址。在现实生活中&#xff0c;我们去菜鸟拿快递的时候&#xff0c;是通过取件码上的编号到架子上…

CLion 解决中文输出乱码的问题

问题介绍 在 Clion 的默认设置下&#xff0c;输出中文会出现乱码&#xff0c;如下 #include <iostream> using namespace std;int main() {cout << "你好" << endl;return 0; }输出 浣犲ソProcess finished with exit code 0解决方案 编码问题…

LangChain - Chain

文章目录 1、概览为什么我们需要链? 2、快速入门 (Get started) - Using LLMChain多个变量 使用字典输入在 LLMChain 中使用聊天模型&#xff1a; 3、异步 API4、不同的调用方法__call__调用仅返回输出键值 return_only_outputs只有一个输出键 run只有一个输入键 5、自定义cha…

git submodule---同步最新的内容

0 Preface/Foreword 1 同步最新submodule内容到repo中 项目的repo包含了一个子模块&#xff0c;在开发过程中&#xff0c;经常需要同步子模块最新的commit到repo中。该如何操作呢&#xff1f; 本地在克隆时候&#xff0c;已经同步把子模块中的内容克隆下来了&#xff0c;但是…

Spring 之 IoC概述

目录 1. IoC概述 1.1 控制反转 1.2 依赖注入 2. IoC容器在Spring中的实现 2.1 BeanFactory 2.2 ApplicationContext 2.2.1 ApplicationContext的主要实现类 1. IoC概述 全称&#xff1a;Inversion of Control&#xff0c;译为 “控制反转” Spring通过IoC容器来管理所有…

【LAMMPS学习】八、基础知识(1.6) LAMMPS 与其他代码耦合

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语&#xff0c;以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…

基于拉格朗日分布算法的电动汽车充放电调度MATLAB程序

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 程序简介 该模型主要做的是基于拉格朗日分布算法的电动汽车充放电调度模型。利用蒙特卡洛模拟法模拟出电动汽车负荷曲线&#xff0c;并求解出无序充电功率曲线和有序充电曲线&#xff0c;该模型在电动汽车个…

标准C库文件操作

open 系列API 和 fopen系列API的区别 1.来源: -open 是UNIX系统调用函数(包括LINUX系统)&#xff0c;返回的是文件描述符 -fopen是ANSIC标准的C语言库函数&#xff0c;在不同系统重调用不同内核的API 2.移植性: fopen 是C标准函数&#xff0c;具有良好的移植性&#xff1b; 而…

JUC-线程的创建、运行与查看

创建和运行线程 Thread创建线程 Thread 创建线程方式&#xff1a;创建线程类&#xff0c;匿名内部类方式 start() 方法底层其实是给 CPU 注册当前线程&#xff0c;并且触发 run() 方法执行线程的启动必须调用 start() 方法&#xff0c;如果线程直接调用 run() 方法&#xff…

【鸿蒙开发】组件状态管理@Prop,@Link,@Provide,@Consume,@Observed,@ObjectLink

1. Prop 父子单向同步 概述 Prop装饰的变量和父组件建立单向的同步关系&#xff1a; Prop变量允许在本地修改&#xff0c;但修改后的变化不会同步回父组件。当父组件中的数据源更改时&#xff0c;与之相关的Prop装饰的变量都会自动更新。如果子组件已经在本地修改了Prop装饰…

01-Git 快速入门

https://learngitbranching.js.org/?localezh_CN在线练习git 1. Git 安装好Git以后, 先检查是否已经绑定了用户名和邮箱 git config --list再检查C:\Users\xxx.ssh 下是否存在 id_rsa.pub , 存在的话复制其内容到 GitHub 的 SSH KEY 中 没有这一步, PUSH操作的时候会报错:…

Altair® (澳汰尔)Inspire™ Render —— 强大的 3D 渲染和动画工具

Inspire Render 是一种全新 3D 渲染和动画工具&#xff0c;可供创新设计师、建筑师和数字艺术家以前所未有的速度快速制作精美的产品演示。 借助基于物理特性的内置高品质全局照明渲染引擎 Thea Render&#xff0c;可以快速创建、修改和拖放各种材质并添加照明环境&#xff0c…

【JavaWeb】Day34.MySQL概述——数据库设计-DDL(一)

项目开发流程 需求文档&#xff1a; 在我们开发一个项目或者项目当中的某个模块之前&#xff0c;会先会拿到产品经理给我们提供的页面原型及需求文档。 设计&#xff1a; 拿到产品原型和需求文档之后&#xff0c;我们首先要做的不是编码&#xff0c;而是要先进行项目的设计&am…