开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第33天,点击查看活动详情
给你 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
思路
本题可以使用动态规划求解。由题目知nums最大的长度不超过14,我们可以使用一个二进制整数num来表示当前删除过的整数,若num第i位为0,表示还没被删除,i位为1时,表示已被删除。用dp[num]表示操作删除状态为num时的最大得分。设num中1的个数为t且x位和y位为1(x != y),因为每次操作都是两个数字,所以t必须为偶数。dp[num]的一个候选值是dp[num - 2^x - 2^y] + gcd(nums[x], nums[y]) * t/2,我们遍历所有num中为1的位数,求出候选值找那个的最大值就是dp[num]的值。
dp[num] = max(dp[num - 2^x -2^y] + gcd(nums[x], nums[y]) * t/2)
dp[0]=0表示一个数字都没被删除,得分为0,dp[2^len(nums)]表示nums中所有数字都被删除了,就是我们要求的值。
解题
/**
* @param {number[]} nums
* @return {number}
*/
var maxScore = function (nums) {
const n = nums.length;
const baseNum = Math.pow(2, n);
const gcd = (num1, num2) => {
while (num2 !== 0) {
const temp = num1;
num1 = num2;
num2 = temp % num2;
}
return num1;
};
const bitCount = (num) => {
return num.toString(2).split("0").join("").length;
};
const gcdNums = new Array(n).fill(null).map(() => new Array(n).fill(1));
for (let i = 0; i < n - 1; i++) {
for (let j = i + 1; j < n; j++) {
gcdNums[i][j] = gcd(nums[i], nums[j]);
}
}
const dp = new Array(baseNum).fill(0);
for (let i = 1; i < baseNum; i++) {
const t = bitCount(i);
if (t & 1) continue;
for (let j = 0; j < n - 1; j++) {
if ((i & (1 << j)) === 0) continue;
for (let k = j + 1; k < n; k++) {
if ((i & (1 << k)) === 0) continue;
let idx = i - (1 << j) - (1 << k);
const total = dp[idx] + (t >> 1) * gcdNums[j][k];
dp[i] = Math.max(dp[i], total);
}
}
}
return dp[baseNum - 1];
};