2560. 打家劫舍 IV(二分)

简介: 2560. 打家劫舍 IV(二分)

题目描述:

沿街有一排连续的房屋。每间房屋内都藏有一定的现金。现在有一位小偷计划从这些房屋中窃取现金。

由于相邻的房屋装有相互连通的防盗系统,所以小偷 不会窃取相邻的房屋

小偷的 窃取能力 定义为他在窃取过程中能从单间房屋中窃取的 最大金额

给你一个整数数组 nums 表示每间房屋存放的现金金额。形式上,从左起第 i 间房屋中放有 nums[i] 美元。

另给你一个整数 k ,表示窃贼将会窃取的 最少 房屋数。小偷总能窃取至少 k 间房屋。

返回小偷的 最小 窃取能力。

示例 1:

输入:nums = [2,3,5,9], k = 2

输出:5

解释:

小偷窃取至少 2 间房屋,共有 3 种方式:

- 窃取下标 0 和 2 处的房屋,窃取能力为 max(nums[0], nums[2]) = 5 。

- 窃取下标 0 和 3 处的房屋,窃取能力为 max(nums[0], nums[3]) = 9 。

- 窃取下标 1 和 3 处的房屋,窃取能力为 max(nums[1], nums[3]) = 9 。

因此,返回 min(5, 9, 9) = 5 。

示例 2:

输入:nums = [2,7,9,3,1], k = 2

输出:2

解释:共有 7 种窃取方式。窃取能力最小的情况所对应的方式是窃取下标 0 和 4 处的房屋。返回 max(nums[0], nums[4]) = 2 。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 109
  • 1 <= k <= (nums.length + 1)/2

思路:

还是打家劫舍,但是题目意思还是变了很多的,现在是要求选出至少k个房子,每选出k个房子里面都存在一个偷窃能力值,而这个偷窃能力值就是这至少k个房子里金钱的最大值(并不是和),最终答案是在所有选至少k个房子的方案里面,所有偷窃能力值的最小值。当然,还是不能选相邻的房子。

这题简单的来说就是求每一个符合选法方案的最大值集合里的最小值。

可以理解为在一个班级里面选k个人,把这k个人里最胖的拿出来,一个班里面选至少k个人的所有选法,每个选法都要拿出一个胖子,最后把这些胖子放到一个空地,选一个最瘦的。对答案就是求这些胖子里面最瘦的。

简而言之,就是求最大值的最小值。

遇到这种问题要瞬间想到二分。

为什么要用二分去做呢?

因为我们根据二分的思想,二分答案,把可以为答案的放在右边,把不可能为答案的方案放在左边,这么一来,在可以为答案的集合里面,最左边的就是这个答案集合里面最小的。

那我们怎么去判断当前二分的偷窃能力值可不可以符合某一个选法方案呢?

我们可以遍历整个数组,用一个num记下符合选法的数量。

当遇到一个数小于或者等于mid(当前二分的答案)s时,我们就num++,需要注意的是,我们不能选相邻的房子,所以需要用一个变量pos去记录上一个放入方案的下标,如果相邻就跳过就行了。最后判断num是否大于或者等于k,小于k说明不可能选出来方案的偷窃能力值小于等于mid,因为要想答案最大为mid,选出来的方案里面的房子的金钱就不能大于mid.

因为答案一定是数组中的某个值,所以二分的左右边界应该就是数组中的最小值和最大值。

代码:

class Solution {
public:
    int minCapability(vector<int>& nums, int k) {
        int mi = 1e9;//左边界
        int mx = 0;//右边界
        for (auto it : nums) {
            mi = min(mi, it);
            mx = max(mx, it);
        }
        int len = nums.size();
        int l = mi - 1;
        int r = mx + 1;
        while (l + 1 != r) {
            int mid = l + r >> 1;
            int num = 0;
            int pos = -1;//记录上一个选入方案的下标
            for (int i = 0; i < len; i++) {
                if (nums[i] <= mid && (pos == -1 || i - pos > 1)) {//选入方案的数必须小于或者等于mid,而且不与pos相邻
                    num++;                                       //第一次选要特判一下
                    pos = i;
                }
            }
            if (num >= k)r = mid;//符合答案的放右边
            else l = mid;
        }
        return r;//此时的r表示所有合格答案的左边界
    }
};


相关文章
|
5月前
【每日一题Day331】LC2560打家劫舍 IV | 二分查找 + 贪心
【每日一题Day331】LC2560打家劫舍 IV | 二分查找 + 贪心
35 0
|
5月前
leetcode-6111:螺旋矩阵 IV
leetcode-6111:螺旋矩阵 IV
39 0
|
5月前
|
人工智能 算法 测试技术
map|动态规划|单调栈|LeetCode975:奇偶跳
map|动态规划|单调栈|LeetCode975:奇偶跳
|
5月前
|
缓存 算法 测试技术
【单调栈】【二分查找】LeetCode: 2454.下一个更大元素 IV
【单调栈】【二分查找】LeetCode: 2454.下一个更大元素 IV
|
5月前
|
算法 C#
Leetcode算法系列| 5. 最长回文子串
Leetcode算法系列| 5. 最长回文子串
|
5月前
【每日一题Day122】LC1237找出给定方程的正整数解 | 双指针 二分查找
【每日一题Day122】LC1237找出给定方程的正整数解 | 双指针 二分查找
37 0
|
12月前
poj 1088 记忆化搜索||动态规划
记忆化搜索也也是采用递归深搜的对数据进行搜索,但不同于直接深搜的方式,记忆化搜索是在每次搜索时将得到的结果保存下来,避免了重复计算,这就是所谓的记忆化。记忆化应该是属于动态规划。
35 0
|
12月前
|
存储 算法 C++
剑指offer(C++)-JZ69:跳台阶(算法-动态规划)
剑指offer(C++)-JZ69:跳台阶(算法-动态规划)
|
存储
leetcode 2560. 打家劫舍 IV
leetcode 2560. 打家劫舍 IV
63 0
|
算法
leetcode-每日一题873. 最长的斐波那契子序列的长度(哈希和二分)
题目要求斐波那契数列长度要大于等于3,就等于说要确定 x[1] 和 x[2]来确定x[3]…x[n]之和的数列,所以我们用两层for循环来枚举x[1] 和 x[2] ,因为斐波那契数列满足 x[i] = x[i - 1] + x[i - 2], 所以x[3] = x[1] + x[2] x[4] = x[3] + x[2]…,我们只需要三个变量来不断变换, 每次从 arr 中找前两个数,然后查看后续在符合斐波那契的数在arr中是否存在
101 0
leetcode-每日一题873. 最长的斐波那契子序列的长度(哈希和二分)