N 次操作后的最大分数和
难度:
标签:
题目描述
代码结果
运行时间: 756 ms, 内存: 16.5 MB
/*
思路:
1. 计算数组中所有可能的两两元素的最大公约数,并存储在一个表中。
2. 使用回溯法选择不同的元素对来计算得分,并记录最大得分。
3. 每次操作时,我们选择剩余元素中的最大公约数对,计算得分并删除这些元素。
Java Stream 版本:利用递归生成所有的子集组合,计算最大得分。
*/
import java.util.*;
import java.util.stream.IntStream;
public class MaxScoreStream {
public int maxScore(int[] nums) {
int n = nums.length / 2;
return maxScore(nums, (1 << nums.length) - 1, new int[1 << nums.length]);
}
private int maxScore(int[] nums, int mask, int[] dp) {
if (dp[mask] != 0) return dp[mask];
int pairs = Integer.bitCount(mask) / 2;
return dp[mask] = IntStream.range(0, nums.length).filter(i -> (mask & (1 << i)) != 0)
.boxed().flatMap(i -> IntStream.range(i + 1, nums.length)
.filter(j -> (mask & (1 << j)) != 0)
.mapToObj(j -> new int[]{i, j}))
.mapToInt(pair -> pairs * gcd(nums[pair[0]], nums[pair[1]]) + maxScore(nums, mask ^ (1 << pair[0]) ^ (1 << pair[1]), dp))
.max().orElse(0);
}
private int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
}
解释
方法:
该题解采用了动态规划与预处理的最大公约数(GCD)的方法。首先,数组被排序,然后计算并存储任意两个元素之间的GCD到一个二维数组中,以减少后续计算中的重复工作。接着,定义一个dp数组,其索引代表一个二进制数,二进制数的每一位表示数组中对应位置的元素是否已经被选择(1代表选择,0代表未选择)。dp数组中的值存储到目前为止可获得的最大分数。对于dp数组的每个状态,如果选取的元素个数是偶数(即可以完全成对),则尝试所有可能的配对方式,计算可能的分数并更新dp状态。最后,dp数组的最后一项(所有元素都被选择时的状态)就是最大分数。
时间复杂度:
O(2^(2n) * n^2)
空间复杂度:
O((2n)^2 + 2^(2n))
代码细节讲解
🦆
如何确定初始化`gcds`二维数组时,使用的循环边界确实涵盖了所有必要的元素对?
▷🦆
在动态规划的实现中,为什么选择使用二进制数来代表数组中元素的选取状态?
▷🦆
在题解中提到的`dp[m]`数组更新过程中,`cnt`的计算是如何确保每次都是在正确的基础上增加新的分数的?
▷🦆
在计算`(t>>1) * gcds[x][y]`时,`t>>1`代表了什么含义,它是如何与操作次数`i`联系起来的?
▷