leetcode1283 使结果不超过阈值的最小除数

 

 

这道题第一思路是用二分查找

因为使用二分法:所以复杂度为O(n*logk), k介于 left=sum/threshold(向下取整) 和 right=num_max之间;
而right<=10^6, left>=1; 故logk <=6log2(10) ~=18;

主要是估算可能除数的上下界,上面的估算方法略强于 1~10^6(即不估算);

解释一下上面估算的原因:因为每个元素除以除数d都要向上取整,向上取整结果res=(num+x)/d (此时x介于[0,d),x使得该式余数为0),
相当于将元素放大了,sum_res>=sum 必然存在,因此有 threshold >= ans >= sum_res/d >= sum/d, ans为各元素除以 d以后累加的
真实结果,由于有向上取整,必然要大于等于sum_res/d;经过缩放后可以得到threshold>=sum/d (表达式除法均为c++的向下取整除法),
从而有d>=sum/threshold;
也因为有向上取整,所以只要元素不为0,那么只要d超过了最大值num_max,此时无论d如何增加,最后运算结果ans总是为n-k, n为数组的
长度,而k为数组中零元素的个数;所以可知,d的最大边界为right=num_max;
从而可得二分法的左右边界[left,right] 为 [sum/threshold, num_max];

作者:joelwang
链接:https://leetcode-cn.com/problems/find-the-smallest-divisor-given-a-threshold/solution/er-fen-cha-zhao-beat-100-by-joelwang/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Solution {
public:
int smallestDivisor(vector<int>& nums, int threshold) {
        long int sum=0;
        int right=INT_MIN;
        int res=INT_MAX;
        for(int num:nums){
            sum+=num;
            if(num>right) right=num;
        }
        int left=sum/threshold;
        if(left==0) left=1;

        while(left<right){
            int mid=left+(right-left)/2;

            int tmp=0;
            for(int num:nums){
                tmp+=num/mid+( ( num % mid == 0 ) ? 0 : 1 );
            }
            if(tmp<=threshold) {
                if(mid<res) res=mid;
                right=mid;
            }else{
                left=mid+1;
            }

        }
        return res;
    }
};

 

posted @ 2019-12-09 10:16  Joel_Wang  阅读(317)  评论(0编辑  收藏  举报