leetcode2809. 使数组和小于等于 x 的最少时间 排序+0-1背包

  • https://leetcode.cn/problems/minimum-time-to-make-array-sum-at-most-x/

  • 给你两个长度相等下标从 0 开始的整数数组 nums1 和 nums2 。每一秒,对于所有下标 0 <= i < nums1.length ,nums1[i] 的值都增加 nums2[i] 。操作 完成后 ,你可以进行如下操作:

  • 选择任一满足 0 <= i < nums1.length 的下标 i ,并使 nums1[i] = 0 。
    同时给你一个整数 x

  • 请你返回使 nums1 中所有元素之和小于等于x 所需要的最少时间,如果无法实现,那么返回 -1 。

示例 1:

输入:nums1 = [1,2,3], nums2 = [1,2,3], x = 4
输出:3
解释:
第 1 秒,我们对 i = 0 进行操作,得到 nums1 = [0,2+2,3+3] = [0,4,6] 。
第 2 秒,我们对 i = 1 进行操作,得到 nums1 = [0+1,0,6+3] = [1,0,9] 。
第 3 秒,我们对 i = 2 进行操作,得到 nums1 = [1+1,0+2,0] = [2,2,0] 。
现在 nums1 的和为 4 。不存在更少次数的操作,所以我们返回 3 。
示例 2:

输入:nums1 = [1,2,3], nums2 = [3,3,3], x = 4
输出:-1
解释:不管如何操作,nums1 的和总是会超过 x 。
 

提示:

1 <= nums1.length <= 103
1 <= nums1[i] <= 103
0 <= nums2[i] <= 103
nums1.length == nums2.length
0 <= x <= 106

题解

  • 答案最大为n,因为当操作n+1次时,代表有一个位置操作了两次,则那个位置的第一次操作被浪费掉了。

  • 所以可以遍历1到n,即选i个元素,计算操作之后的最小值

  • 先选择增量值小的(即nums2中值较小的,第一个选择的位置对应的val,会后续增加(i-1)*val,所以先选择nums2中值较小的)

  • dp[i][j] 表示在前 i -1个元素中,清除(收集)j个获得的最大收益

初始化 f[0][0]=0。
考虑第i 个数「选或不选」:
不选:
f[i+1][j]=f[i][j]。
选:
f[i+1][j]=f[i][j−1]+nums1[i]+nums2[i]⋅j。
这两种情况取最大值,即
f[i+1][j]=max(f[i][j],f[i][j−1]+nums1[i]+nums2[i]⋅j)

code

class Solution {
    
    
public:
    int minimumTime(vector<int>& nums1, vector<int>& nums2, int x) {
    
    
        int n = nums1.size();
        typedef pair<int, int> my_pair;
        // 所有元素按 nums2[i] 从小到大排序,保证无后效性
        vector<my_pair> vec;
        for (int i = 0; i < n; i++) 
        	vec.push_back(my_pair(nums2[i], nums1[i]));
        sort(vec.begin(), vec.end());

        // 套用 0-1背包 dp 方程
        const long long INF = 1e18;
        long long dp[n + 1][n + 1];
        for (int i = 0; i <= n; i++) 
            for (int j = 0; j <= n; j++) 
                dp[i][j] = -INF;
        dp[0][0] = 0;
        for (int i = 1; i <= n; i++) 
            for (int j = 0; j <= i; j++) {
    
    
                f[i][j] = f[i - 1][j];
                if (j > 0) 
                    dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + vec[i - 1].second +  j * vec[i - 1].first);
            }

        // 枚举所有操作可能的次数
        long long sm1 = 0, sm2 = 0;
        for (int x : nums1) sm1 += x;
        for (int x : nums2) sm2 += x;
        for (int i = 0; i <= n; i++) {
    
    
            long long t = sm1 + sm2 * i - dp[n][i];
            if (t <= x) return i;
        }
        return -1;
    }
};
  • 维度压缩
class Solution {
    
    
public:
    int minimumTime(vector<int>& nums1, vector<int>& nums2, int x) {
    
    
        int n = nums1.size();
        typedef pair<int, int> my_pair;
        // 所有元素按 nums2[i] 从小到大排序,保证无后效性
        vector<my_pair> vec;
        for (int i = 0; i < n; i++) vec.push_back(my_pair(nums2[i], nums1[i]));
        sort(vec.begin(), vec.end());

        // 套用 dp 方程
        const long long INF = 1e18;
        long long dp[n + 1];
        for (int i = 0; i <= n; i++) 
                dp[i] = -INF;
        dp[0] = 0;
        for (int i = 1; i <= n; i++) 
            for (int j = n; j > 0; j--) {
    
    
                    dp[j] = max(dp[j], dp[j - 1] + vec[i - 1].second +  j * vec[i - 1].first);
            }

        // 枚举所有操作可能的次数
        long long sm1 = 0, sm2 = 0;
        for (int x : nums1) sm1 += x;
        for (int x : nums2) sm2 += x;
        for (int i = 0; i <= n; i++) {
    
    
            long long t = sm1 + sm2 * i - dp[i];
            if (t <= x) return i;
        }
        return -1;
    }
};

猜你喜欢

转载自blog.csdn.net/ResumeProject/article/details/132166042