开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第23天,点击查看活动详情
1799. N 次操作后的最大分数和
难度:困难
题目:
给你 nums ,它是一个大小为 2 * n 的正整数数组。你必须对这个数组执行 n 次操作。
在第 i 次操作时(操作编号从 1 开始),你需要:
- 选择两个元素
x和y。 - 获得分数
i * gcd(x, y)。 - 将
x和y从nums中删除。
请你返回 n 次操作后你能获得的分数和最大为多少。
函数 gcd(x, y) 是 x 和 y 的最大公约数。
示例 1:
输入: nums = [1,2]
输出: 1
解释: 最优操作是:
(1 * gcd(1, 2)) = 1
示例 2:
输入: nums = [3,4,6,8]
输出: 11
解释: 最优操作是:
(1 * gcd(3, 6)) + (2 * gcd(4, 8)) = 3 + 8 = 11
示例 3:
输入: nums = [1,2,3,4,5,6]
输出: 14
解释: 最优操作是:
(1 * gcd(1, 5)) + (2 * gcd(2, 4)) + (3 * gcd(3, 6)) = 1 + 4 + 9 = 14
提示:
1 <= n <= 7nums.length == 2 * n1 <= nums[i] <= 106
个人思路
1.双指针
我们可以先预处理得到数组 nums 中任意两个数的最大公约数,存储在二维数组 g 中,其中 g[i][j]表示 nums[i]和 nums[j]的最大公约数。
然后定义 f[k]表示当前操作后的状态为 kkk 时,可以获得的最大分数和。假设 mmm 为数组 nums 中的元素个数,那么状态一共有 2^m种,即 kkk 的取值范围为[0, 2^m - 1]。
从小到大枚举所有状态,对于每个状态 k,先判断此状态中二进制位的个数 cnt 是否为偶数,是则进行如下操作:
枚举 k 中二进制位为 1 的位置,假设为 i 和 j,则 i 和 j 两个位置的元素可以进行一次操作,此时可以获得的分数为 cnt/2*g[i][j],更新 f[k]的最大值。
最终答案即为f[2^m - 1]。
class Solution {
public:
int maxScore(vector<int>& nums) {
int m = nums.size();
int g[m][m];
for (int i = 0; i < m; ++i) {
for (int j = i + 1; j < m; ++j) {
g[i][j] = gcd(nums[i], nums[j]);
}
}
int f[1 << m];
memset(f, 0, sizeof f);
for (int k = 0; k < 1 << m; ++k) {
int cnt = __builtin_popcount(k);
if (cnt % 2 == 0) {
for (int i = 0; i < m; ++i) {
if (k >> i & 1) {
for (int j = i + 1; j < m; ++j) {
if (k >> j & 1) {
f[k] = max(f[k], f[k ^ (1 << i) ^ (1 << j)] + cnt / 2 * g[i][j]);
}
}
}
}
}
}
return f[(1 << m) - 1];
}
};
每天记录一下做题思路。