当前位置: 首页 > news >正文

25.4.22华为--算法真题整理(2025年4月22日)

120.三角形最小路径和(120.三角形最小路径和)

题目分析:

给定一个三角形,用二维列表triangle表示,现在约定:自顶向下移动,每一步只能移动到下一行中相邻的节点上,即当前行的下标为i,则可移动到下一行下标为i或i+1处。现求自顶向下的最小路径和并输出。

解题思路:

回溯法:(超出时间限制)
  • 回溯的终止条件:达到最下层,尝试更新最短路径和,并返回
  • 回溯的参数:triangle,当前遍历行i,当前下标j,总行数n
  • 回溯的返回值:void
  • 回溯的搜索遍历逻辑:
    • 尝试下一行下标为j处加入路径
    • 尝试下一行下标为j+1处加入路径
    • 返回
class Solution {int res = Integer.MAX_VALUE;int cur = 0;public int minimumTotal(List<List<Integer>> triangle) {backtracking(triangle, 0, 0, triangle.size());return res;}public void backtracking(List<List<Integer>> triangle, int i, int j, int n) {if(i == n) {res = cur < res ? cur : res;return;}cur += triangle.get(i).get(j);backtracking(triangle, i + 1, j, n);backtracking(triangle, i + 1, j + 1, n);cur -= triangle.get(i).get(j);}
}
动态规划法:
  1. dp数组以及下标的含义:假设triangle共有n层,那么任意路径中均有n个节点,定义dp数组,其中dp[i][j]表示走到位置(i, j)的最小路径和。
  2. 递推公式:dp[i][j] = min(dp[i-1][j], dp[i-1][j-1])
  3. dp数组初始化:
    1. 任意第i层的下标为0的节点,均只能从上一层下标为0的节点到达,因此初始化dp[i][0] = dp[i-1][0] + triangle.get(i).get(0),而dp[0][0]就是triangle.get(0).get(0)
    2. 任意第i层的下标为i的节点,只能由第i-1层的下标为i-1处的节点得到。
  1. 确定遍历顺序:观察递推公式,先自顶向下,再从左往右遍历即可,最终从dp[n-1][j]中选出最小者。
class Solution {public int minimumTotal(List<List<Integer>> triangle) {int res = Integer.MAX_VALUE;int n = triangle.size();int[][] dp = new int[n][n];dp[0][0] = triangle.get(0).get(0);for(int i = 1; i < n; i++) {dp[i][0] = dp[i - 1][0] + triangle.get(i).get(0);for(int j = 1; j < i; j++) {dp[i][j] = Math.min(dp[i - 1][j], dp[i - 1][j - 1]) + triangle.get(i).get(j);}dp[i][i] = dp[i - 1][i - 1] + triangle.get(i).get(i);}for(int j = 0; j < n; j++) {res = dp[n - 1][j] < res ? dp[n - 1][j] : res;}return res;}
}
动态规划思路优化:

观察递推公式,发现每一层的dp只与上一层有关,可以考虑化简dp为一维数组。

  1. dp数组以及下标的含义:dp[j]表示到当前层下标为j处的节点的最小路径和
  2. 确定dp数组递推公式:dp[j] += Math.min(dp[j-1], dp[j]) + triangle(i)(j)
  3. dp数组初始化:对第0列和每一层的第i列特殊化,dp[0]最开始初始化为triangle[0][0]
  4. 确定递推方向:如果正序遍历,当尝试更新第i层的dp[j]时,实际上dp[j-1]已被更新,而dp[j]仍是第i-1层的dp[j],使得递推公式失效,因此必须倒序遍历。
class Solution {public int minimumTotal(List<List<Integer>> triangle) {int res = Integer.MAX_VALUE;int n = triangle.size();int[] dp = new int[n];dp[0] = triangle.get(0).get(0);for(int i = 1; i < n; i++) {dp[i] = dp[i - 1] + triangle.get(i).get(i);for(int j = i - 1; j > 0; j--) {dp[j] = Math.min(dp[j], dp[j - 1]) + triangle.get(i).get(j);}dp[0] = dp[0] + triangle.get(i).get(0);}for(int j = 0; j < n; j++) {res = dp[j] < res ? dp[j] : res;}return res;}
}

486.预测赢家(486.预测赢家)

题目分析:

给定一个整数数组nums,现在有玩家1和玩家2,两者轮流进行自己的回合,玩家1先手,两者的初始分值都是0。

每回合,玩家从数组的任意一端取一个数字并将其从数组中移除,且该数字会加到他的得分上,当数组中没有剩余数字可取时,游戏结束。

如果玩家1一定会输(不包括平局),返回false,否则返回true。

注意:假设每个玩家的玩法都会使他的分数最大化。

解题思路:

(参考官方题解)

记玩家1得分为正,记玩家2得分为负,两者分数的总和如果非负,那么玩家1获胜,否则玩家2获胜。

由于每次只能从数组的一端取数,因此可以用left和right来标定nums数组的合法区间。

递归搜索法:

设计一个递归函数,令玩家1和玩家2轮流取数,并用一个变量turn表示是谁的回合,如果是1则为玩家1的回合,如果是-1则为玩家2的回合。

当turn为1时,玩家1从左端或右端选取数并乘上系数1加至该选择的得分上,此时加的是正数;当turn为-1时,玩家2选数,加的是负数。

当left == end时无其他可选,递归结束,将该数乘上系数并直接返回。

由于每个玩家都会选择令他们得分最大化的选择,因此每层递归返回的总分应选取两种方案中的较大者。

class Solution {public boolean predictTheWinner(int[] nums) {return totalScore(nums, 0, nums.length - 1, 1) >= 0;}public int totalScore(int[] nums, int left, int right, int turn) {if (left == right) {return nums[left] * turn;}int scoreLeft = nums[left] * turn + totalScore(nums, left + 1, right, -turn);// 表示从nums左侧选数的总分int scoreRight = nums[right] * turn + totalScore(nums, left, right - 1, -turn);// 表示从nums右侧选数的总分return Math.max(scoreLeft * turn, scoreRight * turn) * turn;// 乘上对应系数,即相对当前玩家的总分,返回时应抵消该系数}
}
动态规划法:

定义一个行数和列数都为n的dp数组:

  1. dp数组以及下标的定义:dp[i][j]表示当数组中剩下的部分为下标i到下标j时,当前玩家与另一个玩家的分数之差的最大值。
  2. 递推公式:当前玩家可以选择nums[i]或nums[j],然后轮到另一个玩家在数组剩下的部分选取数字,则在这两种方案中,当前玩家会选择最优的方案使得自己的分数最大化,因此得到:
    1. dp[i][j] = max(nums[i] - dp[i+1][j], nums[j] - dp[i][j-1]),最后判断dp[0][nums.length - 1]是否非负,如果非负则先手赢,否则后手赢。
  1. dp数组初始化:当i > j时,dp[i][j] = 0。当i = j时,表示只剩一个数字,则dp[i][i] = nums[i]
  2. 确定遍历方向:计算dp第i行的值时,要用到第i行和第i+1行的值,所以对i倒序遍历;计算dp第j列的值时,鸭涌道第j列和第j-1列的值,所以对j正序遍历。
class Solution {public boolean predictTheWinner(int[] nums) {int n = nums.length;int[][] dp = new int[n][n];for(int i = 0; i < n; i++) {dp[i][i] = nums[i];}for(int i = n - 2; i >= 0; i--) {for(int j = i + 1; j < n; j++) {dp[i][j] = Math.max(nums[i] - dp[i + 1][j], nums[j] - dp[i][j - 1]);}}return dp[0][n - 1] >= 0;}
}
一维动态规划:

第i行只依赖第i+1行和第i行的值,因此可以使用一维数组替代二维数组。

class Solution {public boolean predictTheWinner(int[] nums) {int n = nums.length;int[] dp = new int[n];/*for(int i = 0; i < n; i++) {dp[i] = nums[i];}*/for(int i = n - 1; i >= 0; i--) {dp[i] = nums[i];for(int j = i + 1; j < n; j++) {dp[j] = Math.max(nums[i] - dp[j], nums[j] - dp[j - 1]);}}return dp[n - 1] >= 0;}
}

912.排序数组(912.排序数组)

题目要求:

不使用任何内置函数,用快速排序算法实现升序排列

class Solution {public int[] sortArray(int[] nums) {quickSort(nums, 0, nums.length - 1);return nums;}public void quickSort(int[] nums, int l, int r) {if (l == r) return;int i = l - 1;int j = r + 1;int x = nums[l + r >> 1];while (i < j) {while(nums[++i] < x);while(nums[--j] > x);if (i < j) {int temp = nums[j];nums[j] = nums[i];nums[i] = temp;}}quickSort(nums, l, j);quickSort(nums, j + 1, r);}
}

877.石子游戏(877.石子游戏)

题目分析:

a和b正在用几堆石子做游戏,一共有偶数堆石子,排成一行,每堆石子的个数是正整数,记为piles[i]。

已知石子的总数是奇数,游戏以最终谁手中的石子更多来决定胜负。

a和b轮流进行,a先手。每回合,玩家从行的开始或结束处取走整堆石头,这种情况一直持续到没有更多的石子堆为止。

可以假设两者均做出最优选择,如果a赢得比赛则返回true,b赢得比赛则返回false。

解题思路:

动态规划法:
  1. 确定dp数组以及下标的含义:dp[i][j]表示当剩余石子的区间为[i, j]时,两者总分差的最大值,先手者分高时分差为正,后手者分高时分差为负。
  2. 确定递推公式:当前选手一定选择对于自己来说两种方案之一的最优解,则dp[i][j] = max(piles[i] - dp[i+1][j], piles[j] - dp[i][j-1])
  3. dp数组初始化:当且仅当i >= j时,dp数组有意义,而i == j时,只有一堆石子,则dp[i][i] = piles[i]
  4. 确定遍历方向:观察递推公式,可知i倒序遍历,j正序遍历
class Solution {public boolean stoneGame(int[] piles) {int n = piles.length;int[][] dp = new int[n][n];for(int i = n - 1; i >= 0; i--) {dp[i][i] = piles[i];for(int j = i + 1; j < n; j++) {dp[i][j] = Math.max(piles[i] - dp[i+1][j], piles[j] - dp[i][j-1]);}}return dp[0][n - 1] >= 0;}
}
一维动态规划:
class Solution {public boolean stoneGame(int[] piles) {int n = piles.length;int[] dp = new int[n];for(int i = n - 1; i >= 0; i--) {dp[i] = piles[i];for(int j = i + 1; j < n; j++) {dp[j] = Math.max(piles[i] - dp[j], piles[j] - dp[j-1]);}}return dp[n - 1] >= 0;}
}
数学性质巧解法:

这道题和486.预测赢家非常像,但包含了两个特殊条件:

  1. piles长度为偶数
  2. piles的数组和为奇数

因此,可以将piles按照下标的奇偶性分成两组,那么每当先手的a选取时,剩余piles的头、尾石子堆分属不同组;而当后手的b选取时,剩余piles的头、尾石子堆一定属于同组(奇偶性相同)。

那么我们可以假设,先手的a一开始从奇数组或偶数组中选取石子堆时,后续就一直从这一组中选取,那么后手的b一定只能从另一组中选取,由于piles数组和为奇数且奇数组和偶数组的元素个数相同,必然存在一组的数组和更大,因此可以直接返回true

class Solution {public boolean stoneGame(int[] piles) {return true;}
}

相关文章:

  • 全本地化智能数字人
  • 一个 HTTP 请求进入 Spring MVC 应用后,大致经历了哪些主要步骤?
  • 电商平台数据采集与 QPS 计算全流程解析
  • 逻辑思维与软件开发:从选定方向到风险管理的全流程
  • Linux DRM显示驱动框架技术总结
  • 进阶篇 第 5 篇:现代预测方法 - Prophet 与机器学习特征工程
  • 今日CSS笔记
  • SAS宏调试:高效定位与解决典型问题
  • WLAN 漫游技术全解析:类型、转发模式与应用场景
  • 深度学习--卷积神经网络数据增强
  • 【Linux网络】构建基于UDP的简单聊天室系统
  • python入门简介
  • 课时一 平面机构的自由度与速度分析(上)
  • c语言修炼秘籍 - - 禁(进)忌(阶)秘(技)术(巧)【第七式】程序的编译
  • 生产环境大数据平台权限管理
  • python数据分析(二):Python Pandas索引技术详解
  • 7.6 GitHub Sentinel后端API实战:FastAPI高效集成与性能优化全解析
  • MuJoCo中的机器人状态获取
  • 【教程】安装 iterm2 打造漂亮且高性能的 mac 终端
  • 含锡废水具有显著的回收价值
  • 2025年中央金融机构注资特别国债发行,发行金额1650亿
  • 重庆一幼儿园回应招聘硕士幼教:统一标准,江北区学前教育岗的硬性要求
  • 用一生走丝路,91岁艺术家耿玉琨的书旅奇遇
  • 南京84.57亿元成交8宗宅地:仅秦淮区一宗地块溢价成交
  • 兰斯莫斯想在雅典卫城拍《拯救地球》,希腊当局:价值观不符
  • 周继红连任中国跳水协会主席