题目简意:
给出 \(n\) 个物品,体积为 \(w_i\) ,现把其分成若干组,要求每组总体积 \(V \leq W\) ,问最小分组数。
看这个题意,大概率是一个DP,看一下 \(n\) 的数据范围,点明了记忆化搜索和状压DP两种方法
这篇题解来讲后者
首先确定状态,每一个正整数状态 \(s\) 的在二进制下的第 \(i-1\) 位表示是否有第 \(i\) 个物品
那么 \(f_s\) 表示放入物品状态 \(s\) 时的最优解
显然要开一个新数组维护其剩余体积判断其情况,我们将其设为 \(g_s\) ,表示状态为 \(s\) 时最优解的最后剩余体积
转移方程:
\[f[i|(1<<(j-1))] = f[i] \ \ \ (f[i|(1<<(j-1))] > f[i] \& g[i]\geq w[j]) \]
\[g[i|(1<<(j-1))] = \max g[i]-w[j] \]
\[f[i|(1<<(j-1))] = f[i]+1 \ \ \ (f[i|(1<<(j-1))] > f[i]+1 \& g[i] < w[j]) \]
\[g[i|(1<<(j-1))] = \max W - w[j] \]
这里补充在转移方程中对于 \(g\) 的说明
有人会问: 「这里的 \(g\) 应该和 \(f\) 一起更新,为什么取 \(\max\) ,我试了下也对」
答: 「我们应该弄清楚我们求的是最小方案数,在 \(f_i\) 大小相同的情况下,当然有最大的 \(g_i\) 才能放更多的物品,才是最优子结构」
代码给上,自动查锅
#include<bits/stdc++.h>
using namespace std;
int n,w;
int a[23],f[1<<18],g[1<<18];
int main() {
scanf("%d%d",&n,&w);
for(int i=1;i<=n;++i) scanf("%d",a+i);
memset(f,0x3f,sizeof f); // 初始化最大值
f[0] = 1,g[0] = w; // 别忘了
for(int i=0;i<=(1<<n)-1;++i) {
for(int j=1;j<=n;++j) {
if((1<<(j-1))&i) continue ;
if(a[j]<=g[i] && f[i|(1<<(j-1))] >= f[i]) {
f[i|(1<<(j-1))] = f[i];
g[i|(1<<(j-1))] = max(g[i|(1<<(j-1))],g[i]-a[j]);
} else if(a[j]>g[i] && f[i|(1<<(j-1))] >= f[i]+1) {
f[i|(1<<(j-1))] = f[i]+1;
g[i|(1<<(j-1))] = max(g[i|(1<<(j-1))],w-a[j]);
}
}
}
printf("%d",f[(1<<n)-1]); // 最终结果就是全部放入,即0 ~ n-1位都为1
return 0;
}