22.1.23

一.dp学习

1.多重背包问题

二进制优化:给一个数s,使得0~s间的每个数都可以用2的整数幂来表示;

例如10,用1,2,4,可以表示0~7的所有数,加个3,就可以表示0~10的所有数了;那既然叫二进制优化,那和二进制是有密切关系的;

11=1011(B)=0111(B)+(11−0111(B))=0111(B)+0100(B)

正常背包的思路下,我们要求出含这组商品的最优解,我们要枚举12次(枚举装0,1,2....12个)。

现在,如果我们把这11个商品分别打包成含商品个数为1个,2个,4个,4个(分别对应0001,0010,0100,0100)的四个”新的商品 “, 将问题转化为01背包问题,对于每个商品,我们都只枚举一次,那么我们只需要枚举四次 ,就可以找出这含组商品的最优解。 这样就大大减少了枚举次数。

其中0111通过枚举这三个1的取或不取(也就是对0001(B),0010(B),0100(B)的组合),可以表示十进制数0~7( 刚好对应了 1,2,4 可以组合出 0~7 ) , 0~7的枚举再组合上0100(B)( 即 十进制的 4 ) ,可以表示十进制数 0~11。其它情况也可以这样证明。

那么如何合理的划分一个数呢;是因为 0111(B)刚好是小于11的最大的尾部全为1的二进制 ( 按照上面的证明,这样的划分没毛病 ) , 然后那个尾部全为1的数又可以 分解为 0000....1 , 0000....10 , 0000....100 等等。

//设有s个商品,也就是将s划分
for(int k = 1 ; k <= s ;k*=2)
{
    s-=k;
    goods.push_back({v*k,w*k});
}
if(s>0) 
    goods.push_back({v*s,w*s});

 

#include<bits/stdc++.h>
using namespace std;
const int N = 2010;
int f[N],n,m;
struct good
{
    int w,v;
};

int main()
{
    cin>>n>>m;
    vector<good> Good;
    good tmp;

    //二进制处理
    for(int i = 1 ; i <= n ; i++ )
    {
        int v,w,s;
        cin>>v>>w>>s;
        //坑,k <= s
        for(int k = 1 ; k <= s ; k*=2 )
        {
            s-=k;
            Good.push_back({k*w,k*v});
        }
        if(s>0) Good.push_back({s*w,s*v});
    }

    //01背包优化+二进制
    for(auto t : Good)
        for(int j = m ; j >= t.v ; j--)
            f[j] = max(f[j] , f[j-t.v]+t.w ); //这里就是f[j]


    cout<<f[m]<<endl;
    return 0;

}

二.知识区

1.结构体排序:

用sort来实现,但是需要自己手写一个排序函数,根据实际需要来写;

struct ji{
	int a;
	int b;
}r[110];
bool cmp(ji a,ji b){
	return a.a<b.a ;
}
sort(r,r+n,cmp);

通过sort语句,就实现了对r结构体数组的对a的升序排序;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dull丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值