LintCode 1797: optimalUtilization (双指针及Binary Search好题)

本文深入探讨了在两个已排序数组中寻找两数之和最大且不超过特定阈值k的最优资源利用问题。通过双指针法和二分查找法详细讲解了解决方案,旨在帮助读者理解并掌握此算法的核心思想。
  1. optimalUtilization

Give two sorted arrays. To take a number from each of the two arrays, the sum of the two numbers needs to be less than or equal to k, and you need to find the index combination with the largest sum of the two numbers. Returns a pair of indexes containing two arrays. If you have multiple index answers with equal sum of two numbers, you should choose the index pair with the smallest index of the first array.

The sum of the two numbers <= k
The sum is the biggest
Both array indexes are kept to a minimum
Example
Example1

A = [1, 4, 6, 9], B = [1, 2, 3, 4], K = 9
return [2, 2]
Example2:

Input:
A = [1, 4, 6, 8], B = [1, 2, 3, 5], K = 12
Output:
[2, 3]

这题标成容易题,我觉得并不好想。特别是Binary Search很不容易调通。网上看到不少解法也不完善,主要是不满足“Both array indexes are kept to a minimum"这个条件。

解法1:双指针指针
这个比较好想。两个指针分别指向A,B,都从0开始,当sum<=K并且sum<maxCount时记录结果。注意这里是用的sum<maxCount,所以当后面有新的B的元素有同样的sum时,也没法更新result,同时i也是从0开始的,这样就保证了两个数组的下标尽量小。
代码如下:

class Solution {
public:
    /**
     * @param A: a integer sorted array
     * @param B: a integer sorted array
     * @param K: a integer
     * @return: return a pair of index
     */
    vector<int> optimalUtilization(vector<int> &A, vector<int> &B, int K) {
        int sizeA = A.size();
        int sizeB = B.size();
        if (sizeA == 0 || sizeB == 0) return vector<int>();
        
        int maxCount = INT_MIN;
        vector<int> result;
        
        for (int i = 0; i < sizeA; ++i) {
            for (int j = 0; j < sizeB; ++j) {
                int sum = A[i] + B[j];
                if (sum <= K) {
                    if (sum > maxCount) {
                        result = {i, j};
                        maxCount = sum;
                    }
                }
            }
        }
        
        return result;
        
    }
};

解法2:Binary Search
A数组从0到n-1遍历,对每个A元素,B数组用二分法找出相应元素,满足sum<=K,同时sum尽量大而且两个数组下标又尽量小。这个其实非常不容易调通,主要是因为有重复元素。
注意:

  1. 用二分法模板时,当sum==K时,记录result,同时end=mid,注意这里不能返回,因为可能还有index更小的B元素也满足条件(重复元素)。
  2. 当while()结束后,还要考虑start和end两个边界条件的情况。这里start<end。我们先考虑start的情况,end的情况只有sum2 > maxCount (而不是>=)才更新result,这样就保证了数组下标最小。
    如果我们不考虑start和end两个边界条件会怎么样呢?
    以input=
    [1, 4, 6, 8]
    [1, 2, 3, 5]
    12
    为例,
    Output
    [3,2] //{8,3}
    Expected
    [2,3] //{6,5}
    即在while()中,当i=2//A[2]=6时,B数组只考虑到了mid=3的情况,没有考虑end=4的情况。
  3. 如果我们最后不考虑重复元素的情况,即尽量把重复元素往前挪动,结果仍然不对。
    以input=
    [1, 1, 3, 3, 6, 11, 12, 14, 15, 18]
    [5, 5, 9, 12, 18]
    9
    为例:
    Output
    [2,1]
    Expected
    [2,0]
    即我们仍应该手动找出重复元素中第一个出现的那个下标。这里应该还可以用Binary Search优化,下次再做。

代码如下:

class Solution {
public:
    /**
     * @param A: a integer sorted array
     * @param B: a integer sorted array
     * @param K: a integer
     * @return: return a pair of index
     */
    vector<int> optimalUtilization(vector<int> &A, vector<int> &B, int K) {
        int sizeA = A.size();
        int sizeB = B.size();
        if (sizeA == 0 || sizeB == 0) return vector<int>();
        
        int maxCount = INT_MIN;
        vector<int> result;
        
        for (int i = 0; i < sizeA; ++i) {
            int start = 0, end = sizeB - 1;
            while(start + 1 < end) {
                int mid = start + (end - start) / 2;
                if (A[i] + B[mid] <= K) {
                    if (maxCount < A[i] + B[mid]) {
                        maxCount = A[i] + B[mid];
                        result = {i, mid};
                    }
                    start = mid;
                } else if (A[i] + B[mid] > K) {
                    end = mid;
                } else {
                    maxCount = K;
                    result = {i, mid};
                    end = mid; 
                }
            }
            
            int sum1 = A[i] + B[start];
            int sum2 = A[i] + B[end];
            if (sum1 <= K) {
                if (sum1 > maxCount) {
                    maxCount = sum1;
                    result = {i, start};
                } else if (sum1 == maxCount) {
                    if (i < result[0] || 
                       (i == result[0] && start < result[1])) {
                       result = {i, start}; 
                    }
                }
            }
            if (sum2 <= K && sum2 > maxCount) {
                maxCount = sum2;
                result = {i, end};
            }
            
            for (int j = result[0] - 1; j >= 0; --j) {
                if (A[j] == A[result[0]]) result[0] = j;
                else break;
            }
            
            for (int j = result[1] - 1; j >= 0; --j) {
                if (B[j] == B[result[1]]) result[1] = j;
                else break;
            }
        }
        return result;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值