0%

2809. 使数组和小于等于 x 的最少时间

2809.使数组和小于等于 x 的最少时间

题目描述:

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

  • 选择任一满足 0 <= i < nums1.length 的下标 i ,并使 nums1[i] = 0

同时给你一个整数 x

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

数据范围:

$1\le nums.len \le 10^3, 1\le nums1[i] \le 10^3, 0\le nums2[i] \le 10^3, 0 \le x\le 10^6$

题解:

首先,每个位置只需要被操作一次,因为如果操作了两次,那么前一次无效,可以直接操作最后一次就行。

而且这个操作顺序应该与 $nums2$ 的大小有关。如果先操作大的,那么操作之后的时间,他会从 $0$ 开始增加,增加的最多。因此应该从小到大操作。

假设进行了 $j$ 次操作,操作的下标分别为 $i_1, i_2, \cdots, i_j$ ,那么对于这些操作,每次可以减少的值为:

从这个角度也可以发现,应该让较大的元素放在后面操作。需要对 $nums_1$ 和 $nums_2$ 按照 $nums_2$ 的元素值从小到大排列。

考虑动态规划,使用 $dp[i][j]$ 表示对于数组 $nums_1$ 的前 $i$ 个元素,进行了 $j$ 次操作,所能减少的元素和的最大值。后面可以枚举操作次数 $j$ ,然后 $sum_1 + j \times sum_2 - dp[n][j]$ 可以得到剩余元素之和。

状态转移方程为:

最后枚举 $j \in [0, n]$ 找到最小的满足 $sum_1 + sum_2 \times j - dp[n][j] \le x$ 的 $j$ 即为答案。

也可以一维优化,因为 $dp[i- 1][j]$ 就是上一行的第 $j$ 列,可以倒序枚举 $j$ 更新 $dp$ 数组。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
auto optimize_cpp_stdio = []()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();
class Solution
{
public:
const static int maxn = 1e5 + 10;
const static int maxm = 1e5 + 10;
const static int INF = 0x3f3f3f3f;
const static long long INF_LL = 0x3f3f3f3f3f3f3f3f;
const static long long mod = 1e9 + 7;
int minimumTime(vector<int> &nums1, vector<int> &nums2, int x)
{
int n = nums1.size();
vector<pair<int, int>> nums(n);
for (int i = 0; i < n; ++i)
{
nums[i].first = nums1[i];
nums[i].second = nums2[i];
}
sort(nums.begin(), nums.end(), [&](const pair<int, int> &x, const pair<int, int> &y)
{ return x.second < y.second; });
vector<vector<int>> dp(n + 1, vector<int>(n + 1));
// dp[i][j]=max(dp[i - 1][j], dp[i - 1][j - 1] + nums1[i] + nums2[i] * j);
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= i; ++j)
{
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] + nums[i - 1].first + nums[i - 1].second * j);
}
}
int sum1 = accumulate(nums1.begin(), nums1.end(), 0);
int sum2 = accumulate(nums2.begin(), nums2.end(), 0);
for (int j = 0; j <= n; ++j)
{
if (sum1 + sum2 * j - dp[n][j] <= x)
{
return j;
}
}
return -1;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
auto optimize_cpp_stdio = []()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();
class Solution
{
public:
const static int maxn = 1e5 + 10;
const static int maxm = 1e5 + 10;
const static int INF = 0x3f3f3f3f;
const static long long INF_LL = 0x3f3f3f3f3f3f3f3f;
const static long long mod = 1e9 + 7;
int minimumTime(vector<int> &nums1, vector<int> &nums2, int x)
{
int n = nums1.size();
vector<pair<int, int>> nums(n);
for (int i = 0; i < n; ++i)
{
nums[i].first = nums1[i];
nums[i].second = nums2[i];
}
sort(nums.begin(), nums.end(), [&](const pair<int, int> &x, const pair<int, int> &y)
{ return x.second < y.second; });
vector<int> dp(n + 1);
// dp[i][j]=max(dp[i - 1][j], dp[i - 1][j - 1] + nums1[i] + nums2[i] * j);
for (int i = 1; i <= n; ++i)
{
for (int j = i; j >= 1; --j)
{
dp[j] = max(dp[j], dp[j - 1] + nums[i - 1].first + nums[i - 1].second * j);
}
}
int sum1 = accumulate(nums1.begin(), nums1.end(), 0);
int sum2 = accumulate(nums2.begin(), nums2.end(), 0);
for (int j = 0; j <= n; ++j)
{
if (sum1 + sum2 * j - dp[j] <= x)
{
return j;
}
}
return -1;
}
};