注意 牛客这道题 会出现物品体积为 0 的情况
😦 离谱!
思路
暴力的跑N次没有物品 i 的01背包时间复杂度为N * N * M=10^9会T
思考可以发现可以利用的子问题:
-
设dp[i][j] 表示不取 i 物品 背包体积大小为 j 的方案数,dp[0][j] 表示每个物品都可以取的背包体积为j的方案数
-
dp[i][j] = dp[0][j] - dp[i][j -a[i] ] (体积为j每个物品都可以选的方案数 - 体积为 j 选了 物品 i的方案数)dp[i][j -a[i] ] 表示:不选物品a[i],体积为 j-a[i]的方案数对吧,那么我把dp[i][j - a[i]]的所有方案都给它额外放进去一个物品a[i],那么这些方案不就变成了体积为j ,且一定选了物品a[i]的方案了嘛
-
j < a[i] 说明此时体积为j 的背包一定选不了 i 物品,此时dp[0][j] 不包括选了 i 物品的方案 ,此时dp[i][j] = dp[0][j]
-
因为考虑到物品体积可能为 0 所以物品体积 j = 0时,也要求一下方案数.
代码
~~ 建议看一下代码也是坑 hehe,都是因为会出现物品体积为0 ~~
我的code 过不了的 呵呵 我怀疑标程也没考虑到 体积为0
的情况 这个情况真的很矛盾
a[i] = 0
dp[i][j] = dp[0][j] - dp[i][j - a[i] ] = dp[0][j] 但选了 物品i的方案并没有被移除 呵呵呵
#include<bits/stdc++.h>
using namespace std;
const int N = 2330;
int dp[N][N];
int a[N];
int main(){
int n, m; cin >> n >> m;
dp[0][0] = 1;
for(int i = 1; i <= n; ++i){
cin >> a[i];
for(int j = m; j >= a[i] ; -- j){
dp[0][j] = (dp[0][j] + dp[0][j - a[i]])%10;
}
}
for(int i = 1; i <= n; ++i){
for(int j = 0; j <= m; ++j)
if(j < a[i])
dp[i][j] = dp[0][j];
else dp[i][j] = (dp[0][j] - dp[i][j -a[i]] + 10) %10;
for(int j = 1; j <= m; ++j)
cout << dp[i][j];
cout << endl;
}
// system("pause");
return 0;
}
看了别人的 hehehe
当 a[i] = 0
ans[j] = dp[j] - ans[j -a[i]] = dp[j] - ans[j] 减了一个不选 i - 1 物品体积为 j 的方案 笑死这是正解?
#include<bits/stdc++.h>
using namespace std;
const int N = 2330;
int dp[N];
int ans[N];
int a[N];
int main(){
int n, m; cin >> n >> m;
dp[0] = 1;
for(int i = 1; i <= n; ++i){
cin >> a[i];
for(int j = m; j >= a[i] ; -- j){
dp[j] = (dp[j] + dp[j - a[i]])%10;
}
}
for(int i = 1; i <= n; ++i){
for(int j = 0; j <= m; ++j)
if(j < a[i])
ans[j] = dp[j];
else ans[j] = (dp[j] - ans[j -a[i]] + 10) %10;
for(int j = 1; j <= m; ++j)
cout << ans[j];
cout << endl;
}
system("pause");
return 0;
}