多重背包

1.这个就是在0 1 背包的条件下 加上每个物品能被多次取到!

优化方式 数学知识来优化 :并不是二进制!

首先我们要搞明白多重背包问题,总点就是状态的更新,是以一个什么方式递推过去的,举个例子如果是L[i]个物品,如果是从1-L[i]枚举过去我们不难知道其实,我们这样计算的其实是有一共ΣL[i]1 件物品了,所以并不是我们所需要的情况所以我们需要的是更新1,2,3,4,5,6,7...L[i],这些情况那就要保证我们枚举的时候是刚好可以覆盖掉这些情况 ,需要一定的数学知识:                                                                                                                     

    for(i=1;i<=n;i++)
    {
        int res=l[i];
        for(k=1;k<=res;res-=k,k*=2)
        for(j=m;j>=k*v[i];j--)
        f[j]=max(f[j],f[j-v[i]*k]+w[i]*k);
        for(j=m;j>=res*v[i];j--)
        f[j]=max(f[j],f[j-v[i]*res]+w[i]*res);
    }

这一个 每个数都可以被 1 2 4 8 ....构成 最多再加上一个余数  用一个递增的序列 !!

其中这里面有一个很有趣的点 如你要有三个物品的情况  是  1+2  得到的  因为你在第一次更新过的状态下继续更新同样的 2  就是由2 得到!而3是通过数组组合得到的因为已经考虑了取一个的,那么在取一个的情况下取考虑取两个 那就是三个  ,这个很容易去证明 !!这种写法的正确性 关键就是有点像暴力如何把全部都枚举到!!! 这一点非常非常关键 只要一把他全部跑一遍得到的就算最优解!!

然后还有一个队列优化目前还没研究透彻先贴一份代码来

//太妙了!!很好琢磨琢磨
// % % % % 
#include <bits/stdc++.h>
using namespace std;
int n,m;
int v,l,w,f[10001],c[10001][2];//c[] []  代表的是一个队列!
int main()
{
    scanf("%d%d",&n,&m);
    int i,j,k;
    int x;
    for(i=1;i<=n;i++)
    {
        scanf("%d%d%d",&v,&w,&l);
        for(j=0;j<v;j++)
        {
            int front=1,rear=0;
            for( k=j,x=1 ; k<=m ; x++,k+=v)
            {
                int p=f[k]-x*w,q=x+l;
                for(;front<=rear&&c[rear][0]<=p;rear--);
                c[++rear][0]=p;c[rear][1]=q;
                f[k]=c[front][0]+x*w;        
                for(;front<=rear&&c[front][1]==x;++front);
            }
        }
    }
    cout<<f[m];
}

这个自己细品!!

就基本上就是0 1 背包加上优化!

还有一个特别好的题 思路++ 思路up up !

这个是一个完全背包加上一个分组背包!

不妨设一个 f  ,一个 g 

g 是一个完全背包存找x 元所需的最少硬币

而f 是一个多重背包弄付 x 元所需的最少硬币

这样  f  [ x ] +g [ x-m  ]  元 就是刚好为m的 不断枚举去找最小 

而且这道题的范围数组要开的大小很难确定值得取研究 

这个东西很有意思!!

posted @ 2023-03-13 21:41  _Lance  阅读(102)  评论(0)    收藏  举报