NOIP 2006 金明的预算方案 (裸?)分组背包

或许还有其他方式,不过我就是按分组背包写的,几近裸题。
我也是纳闷,前两天刚刚做过一道分组背包的题,理解了分组背包的正确打开方式,今天却差点没想出来。真是。。。该去睡一会。

对于每个物品以及它的附件,我们把它们拆开,使它们成为独立的物品。因为每个物品最多有两个附件,所以最多会拆成4个新的物品:原主件,原主件+附件1,原主件+附件2,原主件+附件1+附件2,这样一来就没有依赖关系。但是,对于每个物品以及它的附件拆成的4个独立的物品,虽说所有的这样的物品时等价的,但是每4个这样的物品是不可以同时选的。这就是分组背包的模型。

如何避免同一组内的物品被选多个?只需要保证每一个状态不由同一组的转移即可。想一想0-1背包的模型,有两层循环,第一层枚举物品i,第二层枚举背包容量j,如果把两层颠倒,那么结果会变成什么?会导致答案仅仅为所有物品中价值最大的那一个。因为我们是对于容量j枚举物品i,f[j-v[i]]还没有被计算过,或者说,还没有被这一组内的物品计算过,而我们对于0-1背包把两层循环颠倒就相当于把所有物品分成了一组,这一组内只能选一个。

所以,再写一遍,分组背包的解决方式就是,在每个容量下枚举一组内的所有物品,保证同一组内不会有状态的互相转移。在容量之上枚举每一组。

#include <cstdio>
#include <algorithm>
#include <cstring>
#define w0 g[i].pri*g[i].imp
#define v0 g[i].pri
#define w1 g[g[i].c1].pri*g[g[i].c1].imp
#define v1 g[g[i].c1].pri
#define w2 g[g[i].c2].pri*g[g[i].c2].imp
#define v2 g[g[i].c2].pri
using namespace std;

int n, m, e, w[245], v[245];
int f[32005]; 

struct goods{
    int pri, imp;
    int q, c1, c2;
}g[66];

int main()
{
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= m; i++){
        scanf("%d %d %d", &g[i].pri, &g[i].imp, &g[i].q);
        g[g[i].q].c2 = g[g[i].q].c1;
        g[g[i].q].c1 = i;
    }
    for(int i = 1; i <= m; i++){
        if(g[i].q) continue;
        w[++e] = w0;
        v[e] = v0;
        w[++e] = w0 + w1;
        v[e] = v0 + v1;
        w[++e] = w0 + w2;
        v[e] = v0 + v2;
        w[++e] = w0 + w1 + w2;
        v[e] = v0 + v1 + v2;
    }

    for(int i = 1; i <= e; i += 4)
    for(int j = n; j >= 0; j--)
    for(int k = 0; k < 4; k++){
        if(j-v[i+k] < 0) continue;
        f[j] = max(f[j], f[j-v[i+k]] + w[i+k]);
    }
    printf("%d", f[n]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值