摆花初始化dp[0][0]=1,或者dp[0]=1,为什么通天之分组背包不用初始化,而且初始化后只能得3分。
因为通天会访问到dp[0][0]=1,而摆花是计算方案总数,需要初始化,并且不初始化只能得20。通天中,dp[0][0]需要被访问
dp[i][j]=max(dp[i-1][j], dp[i-1][j-w[i][k]]+v[i][k]);
当i=1时,i-1就是0,在继承上一行的过程中就会多算,导致出错。
那为什么样例能对呢?
是因为样例的第二组并没有被使用,而是直接继承,吃不到这个Debuff。
如何升二维
其实很简单,升二维99分的原因就是如果这一组的东西没有被选上,那么就相当于缺失了这一部分。
容量 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
0 | 5 | 5 | 5 | 5 | 7 | 7 | 7 | 7 | |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
那怎么改呢?其实方法简单粗暴,继承下来就好了。
内层循环大于等于1为什么也能得99分
背包容量是零你能装个尖儿啊!?你是穿着开裆裤就想开大车。反正你也用不到零,丢的那一分和上面一样,升二维的问题。
去掉等号以后,为什么只有3分了
#include <bits/stdc++.h>
using namespace std;
int n, m, t, dp[1001][1001], w[1001][1001], v[1001][1001], num[1001];
int main()
{
cin >> m >> n;
for(int i=1; i<=n; ++i){
int ww, vv, group;
cin >> ww >> vv >> group;
num[group]++;
t=max(t, group);
w[group][num[group]]=ww;
v[group][num[group]]=vv;
}
for(int i=1; i<=t; ++i){ //组数
for(int j=m; j>=0; --j){ //背包容量,因为每组只能选1个,所以逆序
//枚举,倒序顺序都行
for(int k=num[i]; k>=1; --k){ //这个不能放在第二层,否则k个物品中会同时选
if(j>w[i][k]){
dp[i][j]=max(dp[i-1][j], dp[i-1][j-w[i][k]]+v[i][k]);
}
}
}
}
cout << dp[t][m];
return 0;
}
因为会重量溢出。比如,我有个宝贝,重达30斤,价值,五亿!我的背包能装50斤,但里面已经有了20斤,如果是<=,那么我可以轻松地装进去而且没有任何的心理负担。但是如果是<,那么我就装不进去,这五亿我就拿不到了,当然会错。
把正确代码的内层和第二层交换以后,为什么98分?
#include <bits/stdc++.h>
using namespace std;
int n, m, t, dp[1001], w[1001][1001], v[1001][1001], num[1001];
int main()
{
cin >> m >> n;
for(int i=1; i<=n; ++i){
int ww, vv, group;
cin >> ww >> vv >> group;
num[group]++;
t=max(t, group);
w[group][num[group]]=ww;
v[group][num[group]]=vv;
}
for(int i=1; i<=t; ++i){ //组数
//枚举,倒序顺序都行
for(int k=num[i]; k>=1; --k){ //这个不能放在第二层,否则k个物品中会同时选
for(int j=m; j>=0; --j){ //背包容量,因为每组只能选1个,所以逆序
if(j>=w[i][k]){
dp[j]=max(dp[j], dp[j-w[i][k]]+v[i][k]);
}
}
}
}
cout << dp[m];
return 0;
}
这个的话,注释里面写的很清楚,k个物品会同时选。为什么,因为它是个枚举!枚举啊,遍历一个组里的所有东西,然后里面还有一层循环。还是上面那个五亿,本来只有一个我背包里装了俩,多出来那一个哪儿来的?他既不符合逻辑也不符合物理对吧。
为什么这个二维代码能满分呢?为什么摆花不需要加内层循环的else部分呢?
摆花求的是方案数
dp[i][j]=(dp[i][j]+dp[i-1][j-k])%1000007;
他直接从上面继承并且直接改变当前位置的值,如果写继承部分的话dp[i][j]它还是本来的样子吗,它都不是本来的值了它还加个什么,能对吗?
为什么二维第二层循环,倒序和顺序都行呢?
倒序
#include <bits/stdc++.h>
using namespace std;
int n, m, t, dp[1001][1001], w[1001][1001], v[1001][1001], num[1001];
int main()
{
cin >> m >> n;
for(int i=1; i<=n; ++i){
int ww, vv, group;
cin >> ww >> vv >> group;
num[group]++;
t=max(t, group);
w[group][num[group]]=ww;
v[group][num[group]]=vv;
}
for(int i=1; i<=t; ++i){ //组数
for(int j=m; j>=1; --j){ //背包容量,因为每组只能选1个,所以逆序
//枚举,倒序顺序都行
for(int k=num[i]; k>=1; --k){ //这个不能放在第二层,否则k个物品中会同时选
if(j>=w[i][k]){
dp[i][j]=max(dp[i-1][j], dp[i-1][j-w[i][k]]+v[i][k]);
}
else{
for(int p=1; p<=i-1; ++p){
dp[i][j]=max(dp[i][j], dp[p][j]);
}
}
}
}
}
cout << dp[t][m] << endl;
return 0;
}
正序
#include <bits/stdc++.h>
using namespace std;
int n, m, t, dp[1001][1001], w[1001][1001], v[1001][1001], num[1001];
int main()
{
cin >> m >> n;
for(int i=1; i<=n; ++i){
int ww, vv, group;
cin >> ww >> vv >> group;
num[group]++;
t=max(t, group);
w[group][num[group]]=ww;
v[group][num[group]]=vv;
}
for(int i=1; i<=t; ++i){ //组数
for(int j=0; j<=m; ++j){ //背包容量,因为每组只能选1个,所以逆序
//枚举,倒序顺序都行
for(int k=num[i]; k>=1; --k){ //这个不能放在第二层,否则k个物品中会同时选
if(j>=w[i][k]){
dp[i][j]=max(dp[i-1][j], dp[i-1][j-w[i][k]]+v[i][k]);
}else{
for(int p=1; p<=i-1; ++p){
dp[i][j]=max(dp[i][j], dp[p][j]);
}
}
}
}
}
cout << dp[t][m];
return 0;
}
呃,其实吧,用正序看似离谱,其实挺合理的。用倒序的原因就是为了保证每组中只选一个。咱捏个样例
45 3
10 10 1
9 5 1
200 400 2
那么在这个样例中,dp数组的前15个元素是什么呢
下标 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 5 | 10 | 10 | 10 | 10 | 10 | 10 |
2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 5 | 10 | 10 | 10 | 10 | 10 | 10 |
看到了吗,他直接被覆盖了。为什么,因为这一行
dp[i][j]=max(dp[i-1][j], dp[i-1][j-w[i][k]]+v[i][k]);
它是个max,dp[i-1][j]是5,dp[i-1][j-w[i][k]]+v[i][k]是0+10,就是10。那肯定舍5而取10者也。那如果你说有个东西重量9价值12,那还考虑什么,拿啊,管你的10|10,我9|12那还不是性价比高,便宜皮实,童叟无欺
还有一个关键原因,就是他是从i-1来取值的!i-1是上一组,我这一组就取不到。还是这个样例。当j=19时,按理说我们应该把两个都拿上,这样价值就是15了。但·是,[0][19]是0啊,0+5还是没有0+10大,正反没区别。
跟你dp,咱心里面儿敞亮。
为啥敞亮你知道吗?
为啥?
咱心里就没自己!
免责声明
该博客的问题来源于礼赞南无大慈大悲大asd菩萨的博客