110场双周赛 T4
Problem: 2809. 使数组和小于等于 x 的最少时间
如何比较两种方案的优劣?
=> 计算相对值往往比绝对值容易
- 答案最大是多少?
假设对一个下标操作了两次,其合并为一次更优
因此答案最大为n
- 从小到大枚举答案
t = 1, 选择当前最大的元素,归零
t = 2,如何比较不同的选取方式的优劣?
假设选取的下标为
i
,j
=> 先
i
后j
or 先j
后i
?
如果有三个下标?
i | j | k | |
---|---|---|---|
1 | 2 * nums2[i] | 1 * nums2[j] | 0 |
2 | 1 * nums2[i] | 2 * nums2[j] | 0 |
3 | 0 | 1 * nums2[j] | 2 * nums2[k] |
4 | 0 | 2 * nums2[j] | 1 * nums2[k] |
5 | 1 * nums2[i] | 0 | 2 * nums2[k] |
6 | 2 * nums2[i] | 0 | 1 * nums2[k] |
可以看出,其实本质上是对i
,j
,k
分配从0开始的系数,从而得出不同的结果。
排序不等式:倒序乘积和 <= 乱序乘积和 <= 正序乘积和
=> 大的系数分配给大的nums2[k]
=> 将
nums1
按照nums2
的大小重新排序,nums2
越小,越先进行操作
原问题
=> 从nums1
中选取一个子序列,子序列的元素分别变为 nums1[i] + j \times nums2[i] (j
为元素在子序列中的索引), 最大化子序列的元素和
=> 最大子序列和变题
=> f[i+1][j]:前i
个数中,选取长度为j
的子序列,求最大的元素和
=> f[i+1][j] = Max(f[i][j], f[i][j-1] + nums1[i] + \times nums2[i])
代码
class Solution {
public int minimumTime(List<Integer> nums1, List<Integer> nums2, int x) {
int n = nums1.size();
int sum1 = 0, sum2 = 0;
var ids = new Integer[n];
for (int i = 0; i < n; i++) {
ids[i] = i;
sum1 += nums1.get(i);
sum2 += nums2.get(i);
}
// 按照nums2的大小, 从小到大排序
Arrays.sort(ids, (i, j) -> nums2.get(i) - nums2.get(j));
// 最大子序和的变体,
// dp[i+1][j]:前i个数中,选取长度为j的子序列,求最大的元素和
int[][] dp = new int[n+1][n+1];
for (int i = 0; i < n; i++) {
for (int j = 1; j <= n; j++) {
dp[i+1][j] = Math.max(dp[i][j], dp[i][j-1] + nums1.get(ids[i])+ j * nums2.get(ids[i]));
}
}
for (int i = 0; i <= n; i++) {
if (sum1 + sum2 * i - dp[n][i] <= x) return i;
}
return -1;
}