一、题目描述
二、题目分析
题目中给出的篮子数目并不多。然而,如果我们分析在 numSlots = 9,nums.size() = 18 这种情况下可能出现的分配方式个数:第一个篮子有 C 18 2 C_{18}^2 C182 中选择,依此类推,共计 12,504,636,144,000 种情况。因此,我们需要压缩所有情况的个数。
关键点1:每个篮子只能放置2个数字
因此,每个篮子中放置数字的个数只可能是 0,1,2 个。这样共有 3 9 = 19683 3^9 = 19683 39=19683 种可能情况。但是如何计算每种情况下的最大与和呢?
关键点2:在放入数字的总个数确定,放入哪些数字也确定的情况下,最优解与数字的放入顺序无关
对于前述的每种情况,我们都可以得到总共放入的数字个数 count。我们可以认为这 count 个数正好是数组的前 count 个值,且第 count 个数值是最后放入的。
那么,这个问题自然而然就变成了:在每种情况下,将最后放入的数字放在哪个可能的篮子中会得到最优解。不难发现,这是一个动态规划问题。
三、算法
使用状态压缩 + 动态规划:
- 如上所述,使用三进制数字表示不同的情况
- 动态规划状态方程为:
s
u
m
[
n
]
=
m
a
x
i
=
1
&
&
n
对
应
的
三
进
制
表
示
中
,
第
i
位
不
为
0
n
u
m
S
l
o
t
s
(
s
u
m
[
n
−
3
i
]
+
n
u
m
s
[
当
前
情
况
下
的
数
字
总
数
−
1
]
&
i
)
sum[n] = max_{i=1 \&\& n对应的三进制表示中,第 i 位不为0}^{numSlots}(sum[n - 3 ^ i] + nums[当前情况下的数字总数 - 1] \& i)
sum[n]=maxi=1&&n对应的三进制表示中,第i位不为0numSlots(sum[n−3i]+nums[当前情况下的数字总数−1]&i)
算法总的复杂度为 3 n u m S l o t s ∗ n u m S l o t s 3^{numSlots} * numSlots 3numSlots∗numSlots。
四、实现
// 1、列出3的整数次幂
vector<int> pow3(numSlots);
int base = 1;
for (int i = 0; i < numSlots; ++i) {
pow3[i] = base;
base *= 3;
}
vector<int> maxAndSums(base);
// 2、遍历所有数字
for (int i = 0; i < base; ++i) {
// 2.1 过滤篮子中的数字总数超出 nums.size() 的情况
int numCount = 0;
int state = i;
while (state > 0) {
numCount += state % 3;
state /= 3;
}
if (numCount > nums.size()) {
continue;
}
// 2.2 状态转移
state = i;
int slotCount = 0;
while (state > 0) {
if (state % 3 > 0) {
maxAndSums[i] = max(maxAndSums[i], maxAndSums[i - pow3[slotCount]] + (nums[numCount - 1] & (slotCount + 1)));
}
state /= 3;
++slotCount;
}
}
return *max_element(maxAndSums.begin(), maxAndSums.end());