花店老板重生之我在洞窟装宝贝(摆花&通天之分组背包)

摆花初始化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分的原因就是如果这一组的东西没有被选上,那么就相当于缺失了这一部分。

容量123456789
055557777
000000000

那怎么改呢?其实方法简单粗暴,继承下来就好了。

内层循环大于等于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个元素是什么呢

下标123456789101112131415
1000000005101010101010
2000000005101010101010

看到了吗,他直接被覆盖了。为什么,因为这一行

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菩萨的博客

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值