【LeetCode-二分法】

二分_元素不在数组中的情况左右返回值.cpp

https://labuladong.github.io/algo/2/18/27/

二分_展厅人数.cpp

// 右边界 容纳最大展厅人数
while (leftLimit < rightLimt) {
    uint32_t midLimit = leftLimit + (rightLimt - leftLimit) / 2;
    if (isFinsh(nums, midLimit, cnt)) {
        leftLimit = midLimit; // 满足的情况,该值后续也需要计算,此处赋值不需要加1,else分支不满足,赋值需要减1
    } else {
        rightLimt = midLimit - 1; // 人数过多,总和大于cnt
    }
}
return leftLimit;


// 左边界 最小负载/电容最小消耗速度
while (left < right) {
    int midSpeed = left + (right - left) / 2; // 下一次的索引区间[left mid] [mid + 1 right]

    if (isFinsh(piles, midSpeed, H)) {
        right = midSpeed; // 所以此处的right 没有 - 1
    } else {
        left = midSpeed + 1;
    }
}
return right; // 返回最小的消耗速度

class Solution {
public:
    int isFinsh(const vector<int> &nums, int midLimit, int cnt)
    {
        int numSunm = 0;
        for (auto num : nums) {
            if (num >= midLimit) {
                numSunm += midLimit;
            } else {
                numSunm += num;
            }
        }
        return numSunm <= cnt ? true : false;
    }

    int ManageTourists(const vector<int> &nums, int cnt)
    {
        uint32_t numsSum = 0;
        for (auto num : nums) {
            numsSum += num;
        }

        if (numsSum <= cnt) {
            return -1;
        }
        
        uint32_t leftLimit = 0;
        uint32_t rightLimt = numsSum;

        // 找右边界
        while (leftLimit < rightLimt) {
            uint32_t midLimit = leftLimit + (rightLimt - leftLimit) / 2;
            if (isFinsh(nums, midLimit, cnt)) {
                leftLimit = midLimit; // 满足的情况,该值后续也需要计算,此处赋值不需要加1,else分支不满足,赋值需要减1
            } else {
                rightLimt = midLimit - 1; // 人数过多,总和大于cnt
            }
        }
        return leftLimit;
    }
};

int main()
{
    // test1:期望 2
    vector<int> input { 1, 4, 2, 5, 5, 1, 6 };
    int cnt = 13;
    // test2:期望 -1
    // vector<int> input { 1,3,2,1 };
    // int cnt = 197;
    // test3:期望 0
    // vector<int> input { 1, 1 };
    // int cnt = 1;
    Solution *s = new Solution();
    int res = s->ManageTourists(input, cnt);
    cout << res << endl;
    system("pause");
    return 0;
}

二分_找目标值左右边界索引(带模板).cpp

class Solution {
public:
    //1 2 2 3 
    //target = 2 return 1 // 返回小于2的元素个数,或者返回最左侧的元素2对应索引
    int leftBound(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size(); // 取数组长度,则while循环left取不到num.size(), 所以while中是 <
        // 找左边界
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] > target) {
                right = mid;
            } else if (nums[mid] < target) {
                left = mid + 1; // left必须是mid + 1,因为遍历的区间是[left, right) 获取到mid后区间被划分成[left, mid) [mid + 1, right)
            } else if (nums[mid] == target) {
                right = mid; // [left, mid) [mid + 1, right) 开区间right, 所以right  = mid
            }
        }

        return right; // 左边界,返回right
    }

    
    int rightBound(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size();  // 索引
        // 找右边界
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] > target) {
                right = mid;
            } else if (nums[mid] < target) {
                left = mid + 1; // 
            } else if (nums[mid] == target) {
                left = mid + 1; // [left, mid) [mid + 1, right) 闭区间left, 所以left  = mid + 1
            }
        }

        return left - 1; // 右侧值减1,原因是获取到mid后区间被划分成[mid + 1, right),导致left的值加1,索引往后移动了1
    }

    vector<int> searchRange(vector<int>& nums, int target) {
        int left = leftBound(nums, target);
        int right = rightBound(nums, target);

        vector<int> res(2, 0);
        if (left <= right && nums[left] == target && nums[right] == target) {
            res[0] = left;
            res[1] = right;
        } else {
            res[0] = -1;
            res[1] = -1;
        }
        return res;
    }
};

int main()
{
    //5,7,7,8,8,10], target = 8
    vector<int> nums = { 5,7,7,8,8,10 };
    Solution *s = new Solution();
    vector<int> res = s->searchRange(nums, 8);

    system("pause");
    return 0;
}

二分法反向操作.cpp

某雪场共有 N 座雪山,数组 altitude中存储了各雪山海拔(精确到整数)。雪场出售新手票与老手票,新手区票价较高。 
若该雪场内最高海拔与最低海拔的差值大于 difference,则为老手区;否则为新手区。现在是滑雪活动旺季,雪场经营者希望获得更大收益,想要将整个雪场打造成新手雪场。
改造某座雪山海拔高度的成本为:变更高度的平方。注意: 变更高度仅可为整数; 变更工程可增加雪山海拔,也可降低雪山海拔; 请问雪场经营者改造需要投入的最少成本是多少(即:所有改造雪山的成本之和)? 
答案需要取模 1e9+71000000007),如计算初始结果为:1000000008,请返回 1。 
示例 1: 输入:altitude = [5,1,4,3,8], difference = 3 输出:8 
解释:将 1 变成 38 变成 6 ,这时最高是 6,最小是 3,相差不超过 3。需要成本为:2^2 + 2^2 = 8 
示例 2: 输入:altitude = [1,2,99999,3,100000], difference = 3 输出:998679962 
解释:将 123 分别变为 40000,将 99999100000 分别变为 40003,此时最高为 40003,最低为 40000,相差不超过 3,此时需要成本为 11998680039,为最小值,取模后为 998679962 提示: 1 <= altitude.length <= 10^5 1 <= altitude[i],difference <= 10^5

思路:取平均数,从下界到上界依次计算总值,取最小。

此题主要注意点在大数表示,用int内部平方相减/相乘很容易整数溢出。

class Solution {
public:
    int minPayment(const vector<int> &altitude, int difference) {
        long sum = accumulate(altitude.begin(), altitude.end(), 0);
        int mean = (int) (sum / altitude.size());
        long long res = INT_MAX;
        long long mod = 1e9 + 7;
        for (int h = mean - difference; h <= mean + difference; h++) {
            long long floor = h;
            long long cell = h + difference;
            long long 0;// 成本累加值可能非常大
            for (auto a : altitude) {
                if (a >= floor && a <= cell) {
                    continue;
                } else if (a > cell) {
                    long long dis = (((long long)a - cell) * ((long long)a - cell));
                    oneCost += dis;
                } else if (a < floor){
                    long long dis = (((long long)a - floor) * ((long long)a - floor));
                    oneCost += dis;
                }
            }
            oneCost %= mod;
            res = std::min(res, oneCost);
        }
        return (int) res;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值