【C++】 —— 笔试刷题day_28

【C++】 —— 笔试刷题day_28

一、游游的重组偶数

题目解析

在这里插入图片描述
这道题,有q组数据,每一次输入一个正整数x,让我们将这个数进行重排,变成一个偶数,然后返回(如果x本身就是一个偶数那可以直接返回x);

如果不存在合法解,就是x通过重排后,无法变成一个偶数,就输出-1

算法思路

这道题,总体来说还是比较简单的;

对于正整数x,我们可以把它当作一个字符串进行输入;(如果按照整数输入,我们还要将这个数x的每一位变换成对应数组)

我们知道,如果一个数是偶数,那最低位一定是一个偶数,这样我们只需判断字符串的最后一位即可知道这个数是否是偶数;如果这个数是偶数,那就直接输出即可;如果最后一位不是偶数,那就从第一位开始向后找,找到一位是偶数,然后把它交换到最后一位;然后输出即可;如果遍历完这个字符串,还没找到一位是偶数的,那就表示这个数x通过重拍无法变成偶数,输出-1即可。

题目解析

#include<iostream>usingnamespace std; string func(){ string str; cin >> str;int n = str.size();if((str[n -1]-'0')%2==0)return str;for(int i =0; i < n -1; i++){if((str[i]-'0')%2==0){swap(str[i], str[n -1]);return str;}}return"-1";}intmain(){int q; cin >> q;while(q--){ cout <<func()<< endl;}return0;}

二、体操队形

题目解析

在这里插入图片描述
dd作为体操队长,现在要对体操队的n名同学(1 ~ n)进行排队;对于第i号队员,会有一个诉求a[i],表示i要排在a[i]的前面;如果a[i] == i,那就表示当前i号队员没有任何诉求,排在哪里都行。

现在dd想知道一共有多少种排队方案。

算法思路

初看这道题,可以说毫无头绪,根本无从下手;

我们要对n个队员排序;一部分队员还要求排在某个队员的前面,对于这个要求,我们很容易就可以解决了:

我们从左到右依次排队即可,排第i个队员时,只需判断它的诉求a[i]有没有排过就OK了。

那对于第i个位置,我们怎们知道某一个队员,能否在这个位置呢?

这就非常简单了,我们只需要记录一下每一个队员是否已经排过队了即可;

我们只需遍历每一个队员,看这一个队员是否能够放到该位置即可;

而我们一个队员不能放到该位置,有两种情况:一是这个队员已经排过了,我们接着遍历下一个队员即可;另一种则是,当前队员的诉求a[i]已经排过了,也就是该队员的诉求[i]在该队员i的前面;那此时无论我们怎们排,那肯定是不满足条件的;那我们就无需向后进行排了。

看到这里,相信大致思路以及能够理清楚了,我们先来绘制一下这道题的一个决策树:

在这里插入图片描述

代码实现

#include<iostream>usingnamespace std;int arr[11];//每一个队员的诉求bool vis[11];//记录每一个队员是否被排过int ret =0;//最终结果int n;voidbfs(int pos){if(pos == n +1){//表示找到一种排序方案 ret++;return;}for(int i =1;i<=n;i++){if(vis[i]==true)//i队员已经被排过continue;if(vis[arr[i]]==true)//i队员的诉求已经排过,也就是arr[i] 在 i的前面了,无论怎么排都不满足条件return; vis[i]=true;//当前位置被排过bfs(pos +1);//排下一个位置 vis[i]=false;//修改,回溯}}intmain(){ cin>>n;for(int i =1;i<=n;i++){ cin>>arr[i];}bfs(1);//从第一个位置开始填 cout<<ret<<endl;return0;}

三、二叉树中的最大路径和

题目解析

在这里插入图片描述
这道题还是比较难理解的,我们来看:

题目给定一个二叉树,让我们求出最大路径和;

路径:我们可以从二叉树中的任意节点出发,通过父 -> 子或者子 -> 父,到达任意节点的序列。同一个节点在一个二叉树路径中最多只能出现一次,简单来说就是同一个节点只能经过一次;一个路径至少包含一个节点,而且不一定要包含跟节点。

就如示例中给的1 , 2 , 3,最优路径就是2 -> 1 -> 3或者3 -> 1 -> 2

算法思路

OK啊,初看这道题,如果题目读的似懂非懂的,那可以说一点思路没有(博主第一次看到这道题就是这样的)

仔细思考一下,这道题不就是一个递归/dfs问题吗;

这里分享一下博主做题时的思路:

对于题目描述,我们要找的路径,它可以是从一个节点先一直向上走,然后在某一个节点,开始再走向子节点;当然也可以是从一个节点一直向上走,不再向下走向子节点。

所以对于二叉树的每一个节点我们可以求一下,在这一个节点为转折点时的最大路径和;

我们要求某一个节点为转折点的最大路径和,那我们就要知道左子树单路径的最大路径和和右子树单路径的最大路径和;我们就只能通过递归的返回值来获得,所以我们返回值就表示从一个节点向下走到nullptr的单路径的最大路径和。

所以在遍历到某一个节点时,要做的就是:求以这个节点为转折点时的最大路径和;然后返回从这个节点向下走到nullptr的单路径的最大路径和。

如果左子树和右子树的最大单路径和是<0的,我们可以不向下走(因为我们要求的是最大路径和);

这样我们只需要考虑最大路径和的第一种情况,就是先向上走,但某一个节点再向下走;对于第二种情况,不就是我们返回值的情况吗。

但是上述中存在一个问题,也是博主在写这道题时没有注意到的一个问题:

这里博主直接判断的是左子树和右子树的最大单路径和都>0时,才计算左右子树最大单路径的和;博主没有考虑到如果左子树和右子树的最大单路径和一个<0,一个小于>0时的情况;

因为博主的疏忽,这也导致博主的思路有一个漏洞:如果考虑我们以某一个节点为转折点的情况,那最大路径和是第二章情况时,我们求出来的就是错误的答案;就比如10 , 5 , -9,正确结果是15而我们求出来的就是10

因为5 -> 10这一种情况在函数最后的返回值当中,而这里没有考虑这一个返回值。(但是这里,是可以通过这道题目的,可以是这道题没有这种情况的测试用例)。

无论如何,博主感觉自己的思路还是存在漏洞的,要在最后考虑一个递归函数的返回值。

OK啊,现在来看这道题的思路:

其实上述就是这道题的大致思路了,哈哈!

我们要求二叉树中的最大路径和,我们要采用递归,bfs;(递归肯定是后序遍历)

那遍历到一个节点时,我们要求什么?

在博主的思路中,求的是以这个节点为转折点的路径和的最大值;

但是如果左子树和右子树某个子树中的最大单路径和<0时,那此时的最大路径和就不是上述中的第一种情况了,而是第二种情况;那我们就漏掉了一直情况。

所以这里我们在遍历到一个节点时,要求的是在该子树中,经过该节点的路径的最大路径和。

那要求经过该节点的最大路径和,我们就要知道左子树和右子树的最大单路径的和

所以我们的返回值就表示该子树中的最大单路径的和,当然要经过当前节点。

OK我们知道了要求什么,和返回值是什么,现在来看如何去求:

经过该节点的最大路径和,那当左右子树最大单路径和可能是小于0的,而<0时,我们肯定不会向下走的,所以我们可以对左右子树的最大单路径和与0求一个最大值;

也就是说当左右子树的最大单路径和是<0时,我们求经过该节点的最大路径和时,左leftright子树最大单路径和是=0的;我们当前节点就表示一个路径。

**返回值:**因为我们这里的leftright都是>=0的,我们直接返回当前节点的值val + max(left,right)即可。

这样最后,我们就无需再对递归函数的返回值就行判断,直接返回结果即可。

代码实现

博主自己的思路:

classSolution{public:int ret =-1000*100000;intfunc(TreeNode* root){if(root ==nullptr)return0;int left =func(root->left);int right =func(root->right);//求if(left >=0&& right >=0) ret =max(ret, left + right + root->val);else ret =max(ret,root->val);returnmax(left,right)+ root->val;}intmaxPathSum(TreeNode* root){returnmax(ret,func(root));}};

优化后的思路代码:

classSolution{public:int ret =-1010;intbfs(TreeNode* root){if(root ==nullptr)return0;int left =max(bfs(root->left),0);int right =max(bfs(root->right),0); ret =max(ret, root->val + left + right);return root->val +max(left, right);}intmaxPathSum(TreeNode* root){// write code herebfs(root);return ret;}};

到这里,本篇文章内容就结束了,感谢各位支持

Read more

Flutter for OpenHarmony: Flutter 三方库 mutex 为鸿蒙异步任务提供可靠的临界资源互斥锁(并发安全基石)

Flutter for OpenHarmony: Flutter 三方库 mutex 为鸿蒙异步任务提供可靠的临界资源互斥锁(并发安全基石)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 虽然 Dart 运行在单线程的事件循环(Event Loop)中,但在处理复杂的异步业务时,我们依然会面临“竞态条件(Race Conditions)”。例如: 1. 文件写入:两个异步任务同时尝试向同一个鸿蒙沙箱文件写入数据。 2. 状态更新:两个 API 回调几乎同时触发,试图修改同一个全局计数器。 3. 数据库操作:在进行“先查询、后更新”的操作间隙,数据被另一个异步流修改了。 mutex 软件包为 Dart 的异步环境提供了经典的“互斥锁”机制。它能确保在任何特定时刻,只有一个异步 Future 能进入被保护的代码块,是保障鸿蒙应用逻辑原子性的核心工具。 一、异步任务排队模型 mutex 强制让交织在一起的异步请求进行“排队”

By Ne0inhk
Ubuntu 24.04 LTS 保姆级教程:安装 NVIDIA 显卡驱动、CUDA 12.5 及 Docker 容器工具包

Ubuntu 24.04 LTS 保姆级教程:安装 NVIDIA 显卡驱动、CUDA 12.5 及 Docker 容器工具包

摘要: 本文为一篇详尽的指南,旨在帮助开发者和研究人员在最新的 Ubuntu 24.04 LTS (Noble Numbat) 系统上,从零开始成功安装 NVIDIA 显卡驱动、CUDA Toolkit 12.5 以及配置 NVIDIA Container Toolkit,从而使 Docker 容器能够利用 GPU 的强大算力。本文适用于深度学习、机器学习、高性能计算等领域的用户。 目录 1. 前言 2. 第一步:环境准备与清理 3. 第二步:添加 NVIDIA CUDA 官方软件源 4. 第三步:安装 NVIDIA 驱动和 CUDA Toolkit 5. 第四步:

By Ne0inhk
Flutter for OpenHarmony:git 纯 Dart 实现的 Git 操作库(在应用内实现版本控制) 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:git 纯 Dart 实现的 Git 操作库(在应用内实现版本控制) 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter for OpenHarmony:git 纯 Dart 实现的 Git 操作库(在应用内实现版本控制) 深度解析与鸿蒙适配指南 前言 Git 通常作为命令行工具存在。但在某些特殊场景下,你可能需要在 App 内部直接操作 Git 仓库,例如: * 开发一个手机端的 Git 客户端 App。 * 使用 Git 作为笔记应用(如 Obsidian)的同步后端。 * 在应用内拉取远程配置或 CMS 内容。 git 是一个纯 Dart 实现的 Git 核心库(类似于 Java 的 JGit)。它负责直接读写

By Ne0inhk

Flutter 三方库 posix 的鸿蒙化适配指南 - 掌控底层系统调用、文件权限管理实战、鸿蒙级系统级工具专家

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 posix 的鸿蒙化适配指南 - 掌控底层系统调用、文件权限管理实战、鸿蒙级系统级工具专家 在鸿蒙跨平台应用开发中,当我们需要实现精密的文件权限操控(如 chmod)、获取系统级用户信息或是管理进程间的信号(Signals)时,高层的 Dart SDK 有时无法提供足够细粒度的控制。如果你需要一种接近 C 语言、直接与鸿蒙内核(Kernel)对话的能力。今天我们要深度解析的 posix——一个旨在为 Dart 提供标准可移植操作系统接口(POSIX)支持的高性能库,正是帮你接管“系统底层主权”的关键插件。 前言 posix 是一套对底层 C 库函数的轻量级封装。它通过 Dart FFI 机制,让你能像写

By Ne0inhk