开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情
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] <= 10^6
思路
看一眼范围,发现最多只有14个数,有多少种方案呢,暴力枚举可行不呢?
一共最多有这么多种方案
化简一下方案数应该是 种方案
看得出来,在1s之内跑完可能不行。
如何优化呢?
换个角度思考。每一层的状态会不会发生重叠,在这个过程中,是不是很多种方案都重复计算了一些答案。
一共有多少种状态?
最多14个数,一共有 种状态。
但是,由于每一层上,选择两个数的代价并不一样。一共有7层,所以,总共完全的状态数量应该是
种状态。
可以看出,总状态数是很少的,并且有些状态根本不会转移到。
于是,方法就明确了,采用记忆化搜索的方式,若是当前dep层的这个状态搜索过(下面的值计算过最大值的情况),直接返回即可。
采用了这个优化之后,本题就变成了一个枚举最多7层的dfs遍历方案数问题。
注意:gcd的运算时间尽管很少,但是可以先算出来,而不是每一次都算一次。
代码
class Solution {
public:
int maxScore(vector<int>& nums) {
int n = nums.size();
vector<vector<int>> back(n/2+1, vector<int>(1<<n));
vector<vector<int>> gcd(n, vector<int>(n));
for (int i = 0; i < n; i ++)
for (int j = i+1; j < n; j ++)
gcd[i][j] = __gcd(nums[i], nums[j]);
function<int(int, int)> dfs
=[&](int dep, int state) -> int {
if (state == 0) return 0;
if (back[dep][state]) return back[dep][state];
int now = 0;
for (int i = 0; i < n; i ++)
if (state >> i & 1)
for (int j = i+1; j < n; j ++)
if (state >> j & 1) {
int mask = state ^ (1 << i) ^ (1 << j);
now = max(now,
dep * gcd[i][j] + dfs(dep + 1, mask));
}
back[dep][state] = now;
return now;
};
return dfs(1, (1<<n) - 1);
}
};