1、状态压缩+动态规划
我们可以使用动态规划来解决问题,我们利用数组 d p [ s ] dp[s] dp[s]来表示状态为s时,能够去到的最大分数。由于题目限定了n的范围,因此我们可以利用二进制数字s来压缩状态,我们利用长度为m的二进制数s的每一位来表示该位上的数字是否被删除,若为1则表示没被删除。
由此我们可以得到状态转移方程为:
d
p
[
s
]
=
m
a
x
{
d
p
[
s
⊕
2
i
⊕
2
J
]
+
t
s
2
×
g
c
d
(
n
u
m
s
[
i
]
,
n
u
m
s
[
j
]
)
}
i
,
j
∈
s
&
i
<
j
dp[s]=max\left \{ dp[s\oplus 2^i\oplus 2^J]+\frac{t_s}{2}\times gcd(nums[i],nums[j]) \right \} i,j\in s \& i<j
dp[s]=max{dp[s⊕2i⊕2J]+2ts×gcd(nums[i],nums[j])}i,j∈s&i<j
其中为了方便计算任意两个数之间的最大公约数,我们可以使用二维数组
g
c
d
t
m
p
gcd_tmp
gcdtmp来记录任意两个数之间的最大公约数。
class Solution {
public:
int gcd(int a, int b) {
if (a < b) swap(a, b);
if (b == 0) return a;
if (a % 2 == 0 && b % 2 == 0) return 2 * gcd(a / 2, b / 2);
if (a % 2 == 0) return gcd(a / 2, b);
if (b % 2 == 0) return gcd(a, b / 2);
return gcd((a + b) / 2, (a - b) / 2);
}
int maxScore(vector<int> &nums) {
int m = nums.size();
vector<int> dp(1 << m, 0);
vector<vector<int>> gcd_tmp(m, vector<int>(m, 0));
for (int i = 0; i < m; ++i) {
for (int j = i + 1; j < m; ++j) {
gcd_tmp[i][j] = gcd(nums[i], nums[j]);
}
}
int all = 1 << m;
for (int s = 1; s < all; ++s) {
int t = __builtin_popcount(s);
if (t & 1) {//若s中1的个数为奇数则说明当前情况不成立跳出循环
continue;
}
for (int i = 0; i < m; ++i) {
if ((s >> i) & 1) {//判断此时第i为上是否为1,为1则继续
for (int j = i + 1; j < m; ++j) {
if ((s >> j) & 1) {
dp[s] = max(dp[s], dp[s ^ (1 << i) ^ (1 << j)] + t / 2 * gcd_tmp[i][j]);
}
}
}
}
}
return dp[all - 1];
}
};