Problem
Solution
此题在01背包的基础上,需要你求出最优选法的方案数
需要另开一个数组来记录方案数。
此处需要注意,虽然是01背包的另一种问法,但此题需要把dp[1…m]初始化为负无穷。这样做的原因是,如果dp数组全部初始化为0,那么dp[k]的含义为容量为k时背包装的最大价值(注意此处不一定被装满了),那么这样就会造成重复计数的问题。若把dp[1…m]初始化为负无穷,那么最大价值一定是从0开始转移的,也就是说若dp[k]是最大价值,那么一定装满了容量k。
#include <cstdio>
#include <climits>
const int N = 1e3 + 5;
const int mod = 1e9 + 7;
const int INF = INT_MIN;
int dp[N];
int g[N];
int main() {
int n, m;
scanf("%d %d", &n, &m);
//若dp[1-m]=0 则dp的含义为容量为k时能装的最大价值 不一定用完k容量
//因此把dp[1-m]=inf 这样处理后 dp[k]的含义为容量恰好为k时能装的最大价值
for (int i = 1; i <= m; ++i) dp[i] = INF;
g[0] = 1;
for (int i = 1; i <= n; ++i) {
int v, w;
scanf("%d %d", &v, &w);
for (int j = m; j >= v; --j) {
int t = dp[j] > (dp[j - v] + w) ? dp[j] : (dp[j - v] + w);
int s = 0;
if (dp[j] == t) s += g[j];
if (dp[j - v] + w == t)s += g[j - v];
s %= mod;
dp[j] = t;
g[j] = s;
}
}
int ma = 0;
for (int i = 0; i <= m; ++i) ma = ma > dp[i] ? ma : dp[i];
int ans = 0;
for (int i = 0; i <= m; ++i) {
if (ma == dp[i]) {
ans += g[i];
ans %= mod;
}
}
printf("%d\n", ans);
return 0;
}
若不改变dp数组的初始化方法,也可以改变方案计数数组g的初始化方法。
dp[k]与g[k]互相对应
dp[k]的初始含义是用前0件物品,去装容量为k的背包,能获得的最大价值。那么肯定有一种装法就是一件物品都不装,价值为0,因此dp数组全部初始化为0,g数组全部初始化为1。再去做01背包即可。
#include <cstdio>
const int N = 1e3 + 5;
const int mod = 1e9 + 7;
int dp[N];
int g[N];
int main() {
int n, m;
scanf("%d %d", &n, &m);
for(int i=0;i<=m;++i) g[i]=1;
for (int i = 1; i <= n; ++i) {
int v, w;
scanf("%d %d", &v, &w);
for (int j = m; j >= v; --j) {
if(dp[j]<dp[j-v]+w)
{
g[j]=g[j-v];
dp[j]=dp[j-v]+w;
}
else if(dp[j]==dp[j-v]+w)
{
g[j]=(g[j]+g[j-v])%mod;
}
}
}
printf("%d\n", g[m]);
return 0;
}