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

第 93 场周赛:二进制间距、重新排序得到 2 的幂、优势洗牌、最低加油次数

Q1、二进制间距

1、题目描述

给定一个正整数 n,找到并返回 n 的二进制表示中两个 相邻 1 之间的 最长距离 。如果不存在两个相邻的 1,返回 0

如果只有 0 将两个 1 分隔开(可能不存在 0 ),则认为这两个 1 彼此 相邻 。两个 1 之间的距离是它们的二进制表示中位置的绝对差。例如,"1001" 中的两个 1 的距离为 3 。

示例 1:

输入:n = 22
输出:2
解释:22 的二进制是 "10110" 。
在 22 的二进制表示中,有三个 1,组成两对相邻的 1 。
第一对相邻的 1 中,两个 1 之间的距离为 2 。
第二对相邻的 1 中,两个 1 之间的距离为 1 。
答案取两个距离之中最大的,也就是 2 。

示例 2:

输入:n = 8
输出:0
解释:8 的二进制是 "1000" 。
在 8 的二进制表示中没有相邻的两个 1,所以返回 0 。

示例 3:

输入:n = 5
输出:2
解释:5 的二进制是 "101" 。

提示:

  • 1 <= n <= 109
2、解题思路
  1. 遍历二进制位:从最低位到最高位逐位检查 n 的二进制表示。
  2. 记录 1 的位置:用一个变量 last 记录上一个 1 的位置。
  3. 计算距离:每遇到一个 1,计算当前 1 和上一个 1 的距离,并更新最大值。
  4. 返回结果:最后返回最大的距离。
3、代码实现
C++
class Solution {
public:int binaryGap(int n) {int last = -1; // 记录上一个 1 的位置int ans = 0;   // 记录最大距离for (int i = 0; n; ++i) {if (n & 1) // 检查当前位是否为 1{if (last != -1) // 如果之前已经有 1{ans = max(ans, i - last); // 计算距离并更新最大值}last = i; // 更新上一个 1 的位置}n >>= 1; // 右移一位, 检查下一位}return ans;}
};
Java
class Solution {public int binaryGap(int n) {int last = -1; // 记录上一个 1 的位置int ans = 0; // 记录最大距离for (int i = 0; n != 0; ++i) {if ((n & 1) == 1) { // 检查当前位是否为 1if (last != -1) { // 如果之前已经有 1ans = Math.max(ans, i - last); // 计算距离并更新最大值}last = i; // 更新上一个 1 的位置}n >>= 1; // 右移一位,检查下一位}return ans;}
}
Python
class Solution:def binaryGap(self, n: int) -> int:last = -1  # 记录上一个 1 的位置ans = 0    # 记录最大距离i = 0while n:if n & 1:  # 检查当前位是否为 1if last != -1:  # 如果之前已经有 1ans = max(ans, i - last)  # 计算距离并更新最大值last = i  # 更新上一个 1 的位置n >>= 1  # 右移一位,检查下一位i += 1return ans
class Solution:def binaryGap(self, n: int) -> int:binary = bin(n)[2:]  # 转换为二进制字符串last = -1  # 记录上一个 1 的位置ans = 0    # 记录最大距离for i, bit in enumerate(binary):if bit == '1':if last != -1:  # 如果之前已经有 1ans = max(ans, i - last)  # 计算距离并更新最大值last = i  # 更新上一个 1 的位置return ans
4、复杂度分析
  • 时间复杂度O(log n),因为需要遍历 n 的二进制位数。
  • 空间复杂度O(1),只使用了常数空间。

Q2、重新排序得到 2 的幂

1、题目描述

给定正整数 n ,我们按任何顺序(包括原始顺序)将数字重新排序,注意其前导数字不能为零。

如果我们可以通过上述方式得到 2 的幂,返回 true;否则,返回 false

示例 1:

输入:n = 1
输出:true

示例 2:

输入:n = 10
输出:false

提示:

  • 1 <= n <= 109
2、解题思路

方法一:回溯法

  1. 生成所有排列:将 n 的数字重新排列,检查是否有排列是 2 的幂。
  2. 避免重复计算:排序数字并跳过重复数字的递归分支。
  3. 检查 2 的幂:使用 n & (n - 1) == 0 判断是否为 2 的幂。
  4. 优化:剪枝(跳过前导零和重复数字)。

方法二:数字频率统计

  1. 预计算所有 2 的幂的数字频率:生成所有 2 的幂(1 到 1e9),统计每个数字的频率。
  2. 比较频率:统计 n 的数字频率,检查是否与某个 2 的幂的频率匹配。
  3. 优化:使用哈希表存储所有 2 的幂的数字频率。
3、代码实现
C++
// 方法1: 回溯
class Solution {
private:vector<int> vis; // 标记数字是否被使用bool isPowerOfTwo(int n) {return (n & (n - 1)) == 0; // 判断是否为 2 的幂}bool backtrack(string& nums, int idx, int num) {if (idx == nums.length()) {return isPowerOfTwo(num); // 检查当前数字是否为 2 的幂}for (int i = 0; i < nums.length(); ++i) {// 跳过前导零、已使用的数字和重复数字if ((num == 0 && nums[i] == '0') || vis[i] ||(i > 0 && !vis[i - 1] && nums[i] == nums[i - 1])) {continue;}vis[i] = 1; // 标记为已使用if (backtrack(nums, idx + 1, num * 10 + (nums[i] - '0'))) {return true; // 找到有效排列}vis[i] = 0; // 回溯}return false;}public:bool reorderedPowerOf2(int n) {string nums = to_string(n);sort(nums.begin(), nums.end()); // 排序以便跳过重复数字vis.resize(nums.length());return backtrack(nums, 0, 0);}
};
// 方法2: 数据频率统计
string countDigits(int n) {string cnt(10, 0); // 统计数字 0-9 的出现次数while (n) {++cnt[n % 10];n /= 10;}return cnt;
}unordered_set<string> powerOf2Digits; // 存储所有 2 的幂的数字频率int init = []() {for (int n = 1; n <= 1e9; n <<= 1) // 预计所有 2 的幂{powerOf2Digits.insert(countDigits(n));}return 0;
}();class Solution {
public:bool reorderedPowerOf2(int n) {return powerOf2Digits.count(countDigits(n)); // 检查是否匹配}
};
Java
// 方法1: 回溯
class Solution {private boolean[] vis;private boolean isPowerOfTwo(int n) {return (n & (n - 1)) == 0;}private boolean backtrack(char[] nums, int idx, int num) {if (idx == nums.length) {return isPowerOfTwo(num);}for (int i = 0; i < nums.length; ++i) {if ((num == 0 && nums[i] == '0') || vis[i] || (i > 0 && !vis[i - 1] && nums[i] == nums[i - 1])) {continue;}vis[i] = true;if (backtrack(nums, idx + 1, num * 10 + (nums[i] - '0'))) {return true;}vis[i] = false;}return false;}public boolean reorderedPowerOf2(int n) {char[] nums = String.valueOf(n).toCharArray();Arrays.sort(nums);vis = new boolean[nums.length];return backtrack(nums, 0, 0);}
}
// 方法2: 数据频率统计
class Solution {private String countDigits(int n) {int[] cnt = new int[10];while (n > 0) {++cnt[n % 10];n /= 10;}return Arrays.toString(cnt);}private static final Set<String> powerOf2Digits = new HashSet<>();static {for (int n = 1; n <= 1e9; n <<= 1) {powerOf2Digits.add(new Solution().countDigits(n));}}public boolean reorderedPowerOf2(int n) {return powerOf2Digits.contains(countDigits(n));}
}
Python
# 方法1: 回溯
class Solution:def reorderedPowerOf2(self, n: int) -> bool:nums = sorted(str(n))  # 排序以便跳过重复数字vis = [False] * len(nums)def backtrack(idx: int, num: int) -> bool:if idx == len(nums):return (num & (num - 1)) == 0  # 判断是否为 2 的幂for i in range(len(nums)):if (num == 0 and nums[i] == '0') or vis[i] or \(i > 0 and not vis[i - 1] and nums[i] == nums[i - 1]):continuevis[i] = Trueif backtrack(idx + 1, num * 10 + int(nums[i])):return Truevis[i] = Falsereturn Falsereturn backtrack(0, 0)
# 方法2: 数据频率统计
class Solution:def reorderedPowerOf2(self, n: int) -> bool:def countDigits(n: int) -> str:cnt = [0] * 10while n:cnt[n % 10] += 1n //= 10return str(cnt)powerOf2Digits = {countDigits(1 << i) for i in range(30)}  # 预计算所有 2 的幂return countDigits(n) in powerOf2Digits
4、复杂度分析
  • 方法一
    • 时间复杂度O(m!)mn 的位数(最坏情况)。
    • 空间复杂度O(m),递归栈和标记数组。
  • 方法二
    • 时间复杂度O(1),预计算后查询是常数时间。
    • 空间复杂度O(1),预计算存储所有 2 的幂的数字频率。

Q3、优势洗牌

1、题目描述

给定两个长度相等的数组 nums1nums2nums1 相对于 nums2优势可以用满足 nums1[i] > nums2[i] 的索引 i 的数目来描述。

返回 nums1任意 排列,使其相对于 nums2 的优势最大化。

示例 1:

输入:nums1 = [2,7,11,15], nums2 = [1,10,4,11]
输出:[2,11,7,15]

示例 2:

输入:nums1 = [12,24,8,32], nums2 = [13,25,32,11]
输出:[24,32,8,12]

提示:

  • 1 <= nums1.length <= 105
  • nums2.length == nums1.length
  • 0 <= nums1[i], nums2[i] <= 109
2、解题思路
  1. 排序并保留原始索引

    • nums1nums2 进行排序,但需要保留原始索引以便最后放置结果。
  2. 贪心匹配

    • 使用双指针,尝试用 nums1 的最小值去匹配 nums2 的最小值。
    • 如果 nums1 的最小值大于 nums2 的最小值,则直接匹配。
    • 否则,用 nums1 的最小值去“消耗” nums2 的最大值(因为无法战胜任何其他数)。
3、代码实现
C++
class Solution {
public:vector<int> advantageCount(vector<int>& nums1, vector<int>& nums2) {int n = nums1.size();vector<int> idx1(n), idx2(n);iota(idx1.begin(), idx1.end(), 0); // 初始化索引 0, 1, 2, ..., n-1iota(idx2.begin(), idx2.end(), 0);// 根据 nums1 和 nums2 的值排序索引sort(idx1.begin(), idx1.end(), [&](int i, int j) { return nums1[i] < nums1[j]; });sort(idx2.begin(), idx2.end(), [&](int i, int j) { return nums2[i] < nums2[j]; });vector<int> ans(n);int left = 0, right = n - 1;for (int i = 0; i < n; ++i) {if (nums1[idx1[i]] > nums2[idx2[left]]) {// 如果 nums1 当前最小值能战胜 nums2 的最小值, 则直接匹配ans[idx2[left]] = nums1[idx1[i]];++left; // 移动 nums2 的指针} else {// 否则, 用 nums1 的最小值去 "消耗" nums2 的最大值ans[idx2[right]] = nums1[idx1[i]];--right; // 移动 nums2 的指针}}return ans;}
};
Java
class Solution {public int[] advantageCount(int[] nums1, int[] nums2) {int n = nums1.length;Integer[] idx1 = IntStream.range(0, n).boxed().toArray(Integer[]::new);Integer[] idx2 = IntStream.range(0, n).boxed().toArray(Integer[]::new);// 根据 nums1 和 nums2 的值排序索引Arrays.sort(idx1, (i, j) -> nums1[i] - nums1[j]);Arrays.sort(idx2, (i, j) -> nums2[i] - nums2[j]);int[] ans = new int[n];int left = 0, right = n - 1;for (int i = 0; i < n; ++i) {if (nums1[idx1[i]] > nums2[idx2[left]]) {// 如果 nums1 当前最小值能战胜 nums2 的最小值,则直接匹配ans[idx2[left]] = nums1[idx1[i]];++left; // 移动 nums2 的指针} else {// 否则,用 nums1 的最小值去“消耗” nums2 的最大值ans[idx2[right]] = nums1[idx1[i]];--right; // 移动 nums2 的指针}}return ans;}
}
Python
class Solution:def advantageCount(self, nums1: List[int], nums2: List[int]) -> List[int]:n = len(nums1)idx1 = sorted(range(n), key=lambda i: nums1[i])  # 根据 nums1 的值排序索引idx2 = sorted(range(n), key=lambda i: nums2[i])  # 根据 nums2 的值排序索引ans = [0] * nleft, right = 0, n - 1for i in range(n):if nums1[idx1[i]] > nums2[idx2[left]]:# 如果 nums1 当前最小值能战胜 nums2 的最小值,则直接匹配ans[idx2[left]] = nums1[idx1[i]]left += 1  # 移动 nums2 的指针else:# 否则,用 nums1 的最小值去“消耗” nums2 的最大值ans[idx2[right]] = nums1[idx1[i]]right -= 1  # 移动 nums2 的指针return ans
4、复杂度分析
  • 时间复杂度O(n log n),排序的时间复杂度。
  • 空间复杂度O(n),需要额外的数组存储索引和结果。

Q4、最低加油次数

1、题目描述

汽车从起点出发驶向目的地,该目的地位于出发位置东面 target 英里处。

沿途有加油站,用数组 stations 表示。其中 stations[i] = [positioni, fueli] 表示第 i 个加油站位于出发位置东面 positioni 英里处,并且有 fueli 升汽油。

假设汽车油箱的容量是无限的,其中最初有 startFuel 升燃料。它每行驶 1 英里就会用掉 1 升汽油。当汽车到达加油站时,它可能停下来加油,将所有汽油从加油站转移到汽车中。

为了到达目的地,汽车所必要的最低加油次数是多少?如果无法到达目的地,则返回 -1

注意:如果汽车到达加油站时剩余燃料为 0,它仍然可以在那里加油。如果汽车到达目的地时剩余燃料为 0,仍然认为它已经到达目的地。

示例 1:

输入:target = 1, startFuel = 1, stations = []
输出:0
解释:可以在不加油的情况下到达目的地。

示例 2:

输入:target = 100, startFuel = 1, stations = [[10,100]]
输出:-1
解释:无法抵达目的地,甚至无法到达第一个加油站。

示例 3:

输入:target = 100, startFuel = 10, stations = [[10,60],[20,30],[30,30],[60,40]]
输出:2
解释:
出发时有 10 升燃料。
开车来到距起点 10 英里处的加油站,消耗 10 升燃料。将汽油从 0 升加到 60 升。
然后,从 10 英里处的加油站开到 60 英里处的加油站(消耗 50 升燃料),
并将汽油从 10 升加到 50 升。然后开车抵达目的地。
沿途在两个加油站停靠,所以返回 2 。

提示:

  • 1 <= target, startFuel <= 109
  • 0 <= stations.length <= 500
  • 1 <= positioni < positioni+1 < target
  • 1 <= fueli < 109
2、解题思路

方法一:动态规划

  1. 定义状态dp[i] 表示加 i 次油能到达的最远距离。
  2. 初始化dp[0] = startFuel,表示不加任何油时能到达的最远距离。
  3. 状态转移:对于每个加油站,如果能到达该加油站,则尝试加油并更新 dp 数组。
  4. 结果检查:遍历 dp 数组,找到最小的 i 使得 dp[i] >= target

方法二:贪心 + 优先队列

  1. 遍历加油站:模拟汽车行驶过程,记录当前燃料和已行驶距离。
  2. 燃料不足时加油:当燃料不足以到达下一个加油站时,从之前经过的加油站中选择油量最大的加油。
  3. 优先队列:使用优先队列存储可加油的加油站油量,每次取最大的进行加油。
3、代码实现
C++
// 方法1: 动态规划
class Solution {
public:int minRefuelStops(int target, int startFuel, vector<vector<int>>& stations) {int n = stations.size();        // 加油站的数量std::vector<long> dp(n + 1, 0); // dp[i] 表示加 i 次油能行驶的最远距离dp[0] = startFuel;              // 初始状态:不加任何油时能行驶的距离// 遍历每个加油站for (int i = 0; i < n; ++i) {// 从后往前更新 dp 数组,避免重复计算for (int j = i; j >= 0; --j) {if (dp[j] >= stations[i][0]) {// 尝试加油, 更新 dp[j+1]dp[j + 1] = max(dp[j + 1], dp[j] + stations[i][1]);}}}// 检查 dp 数组, 找到最小的加油次数for (int i = 0; i <= n; ++i) {if (dp[i] >= target) {return i;}}// 无法到达目的地return -1;}
};
// 方法二: 贪心 + 优先队列 (堆)
class Solution {
public:int minRefuelStops(int target, int startFuel, vector<vector<int>>& stations) {priority_queue<int> pq;  // 最大堆, 存储可加油的油量int ans = 0;             // 记录加油次数int prev = 0;            // 上一个加油站的位置int fuel = startFuel;    // 当前剩余的燃料int n = stations.size(); // 加油站的数量// 遍历加油站, 包括目的地for (int i = 0; i <= n; ++i) {// 当前位置: 如果是最后一个加油站, 则为目的地int curr = (i < n) ? stations[i][0] : target;// 消耗燃料行驶到当前位置fuel -= curr - prev;// 如果燃料不足, 从之前经过的加油站中加油while (fuel < 0 && !pq.empty()) {fuel += pq.top(); // 加最多的油pq.pop();         // 移除已加油的加油站ans++;            // 增加加油次数}// 如果仍然无法到达当前位置, 返回 -1if (fuel < 0) {return -1;}// 如果不是目的地, 将当前加油站的油量加入堆if (i < n) {pq.push(stations[i][1]);prev = curr; // 更新上一个加油站的位置}}return ans;}
}; 
Java
// 方法1: 动态规划
class Solution {public int minRefuelStops(int target, int startFuel, int[][] stations) {int n = stations.length;long[] dp = new long[n + 1];dp[0] = startFuel;for (int i = 0; i < n; ++i) {for (int j = i; j >= 0; --j) {if (dp[j] >= stations[i][0]) {dp[j + 1] = Math.max(dp[j + 1], dp[j] + stations[i][1]);}}}for (int i = 0; i <= n; ++i) {if (dp[i] >= target) {return i;}}return -1;}
}
// 方法二: 贪心 + 优先队列 (堆)
class Solution {public int minRefuelStops(int target, int startFuel, int[][] stations) {PriorityQueue<Integer> pq = new PriorityQueue<>(Collections.reverseOrder());int ans = 0, prev = 0, fuel = startFuel;int n = stations.length;for (int i = 0; i <= n; ++i) {int curr = (i < n) ? stations[i][0] : target;fuel -= curr - prev;while (fuel < 0 && !pq.isEmpty()) {fuel += pq.poll();++ans;}if (fuel < 0) {return -1;}if (i < n) {pq.offer(stations[i][1]);prev = curr;}}return ans;}
}
Python
# 方法1: 动态规划
class Solution:def minRefuelStops(self, target: int, startFuel: int, stations: List[List[int]]) -> int:n = len(stations)dp = [0] * (n + 1)dp[0] = startFuelfor i in range(n):for j in range(i, -1, -1):if dp[j] >= stations[i][0]:dp[j + 1] = max(dp[j + 1], dp[j] + stations[i][1])for i in range(n + 1):if dp[i] >= target:return ireturn -1
# 方法二: 贪心 + 优先队列 (堆)
class Solution:def minRefuelStops(self, target: int, startFuel: int, stations: List[List[int]]) -> int:heap = []ans, prev, fuel = 0, 0, startFueln = len(stations)for i in range(n + 1):curr = stations[i][0] if i < n else targetfuel -= curr - prevwhile fuel < 0 and heap:fuel += -heapq.heappop(heap)ans += 1if fuel < 0:return -1if i < n:heapq.heappush(heap, -stations[i][1])prev = currreturn ans
4、复杂度分析
  • 方法一
    • 时间复杂度O(n^2),其中 n 是加油站数量。
    • 空间复杂度O(n),存储 dp 数组。
  • 方法二
    • 时间复杂度O(n log n),优先队列操作的时间复杂度。
    • 空间复杂度O(n),优先队列的空间。


http://www.dtcms.com/a/349809.html

相关文章:

  • Agent实战教程:LangGraph相关概念介绍以及快速入门
  • Reachability Query
  • 算法每日一题 | 入门-分支结构-肥胖问题
  • 【modbus学习】野火Modbus主机接收到数据后的完整函数调用流程
  • Ajax笔记(上)
  • Vue3在一个对象的list中,找出update_date最大的一条数据
  • MCP 协议原理与系统架构详解—从 Server 配置到 Client 应用
  • vscode--快捷键
  • ESP8266学习
  • 嵌入式开发学习———Linux环境下网络编程学习(六)
  • 驾驭 Glide 的引擎:深度解析 Module 与集成之道
  • 音视频学习(五十六):单RTP包模式和FU-A分片模式
  • ESP32开发WSL_VSCODE环境搭建
  • *解决 Visual Studio 编译时的字符编码问题
  • 41_基于深度学习的小麦病害检测识别系统(yolo11、yolov8、yolov5+UI界面+Python项目源码+模型+标注好的数据集)
  • 服务器(Linux)新账户搭建Pytorch深度学习环境
  • Linux(从入门到精通)
  • Wisdom SSH 核心功能定位与 sudo 权限配置指南
  • 四层Nginx代理日志配置
  • 2024年Engineering SCI2区,面向工程管理的无人机巡检路径与调度,深度解析+性能实测
  • Vue 2 中的 v-model和Vue3中的v-model
  • 设计模式:原型模式(Prototype Pattern)
  • 使用 HandlerMethodReturnValueHandler 在SpringBoot项目 实现 RESTful API 返回值自动封装,简化开发
  • 数据结构青铜到王者第三话---ArrayList与顺序表(2)
  • 零知开源——基于STM32F103RBT6和ADXL335实现SG90舵机姿态控制系统
  • three.js+WebGL踩坑经验合集(9.1):polygonOffsetUnits工作原理大揭秘
  • 【数据结构】LeetCode160.相交链表 138.随即链表复制 牛客——链表回文问题
  • [SC]SystemC动态进程概述及案例
  • LinkedIn 自动消息发送工具
  • 网络编程——TCP、UDP