个人对背包问题的二进制优化问题的理解

完全背包问题是指
有N种物品和一个容量为V的背包,每一种物品都是有无限的个数。也就是从每种物品角度分析,与其相关的策略不是选不选的问题了 已经转换为选多少个的问题了 
其实说到这里你难道不觉得有点像DAG图的银币问题吗 只是那里的权值是1 这里的权值是W 
(其实我一开始在想一个问题 DAG图不是确定起点以及终点的求最小或者最长路径的嘛 这里只是确定了起点 而且终点只是一个范围值 因为有可能是放不满的)
但是再想想DAG图的硬币问题也是放不满的,因为你不能保证每一都能刚刚好筹到N 所以这里完全背包问题和DAG图的硬币问题是一样的,所以当你求最大值的时候就要初始化数组为负无穷但是数组0的地方为0
这样就可以保证上一个是存在的
那么按照DAG图的硬币问题 就是可以处理每一个状态 (每一个状态都是选择每一个背包来计算 然后把个数转换为重量)
for(int i=1;i<=S;i++)
      for(int j=1;j<=n;j++)
        if(i>=V[j])
        {
          min1[i]=Min(min1[i],min1[i-V[j]]+W);
          max1[i]=Max(max1[i],max1[i-V[j]]+W);
        }
时间复杂都是NS




现在我来说说完全背包问题没有优化的思路吧,其实完全背包问题也是有点像01背包问题那样,但是因为背包的数目是无限的 那么转移方程是f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<= v} 每一状态都要处理O(v/c[i])次 那么总的处理次数是超过O(VN)






这里有一个小小的思路吧 假如有两个背包 背包体积第一个比第二个小 并且第一个背包的价值比第二个大 那么就要舍弃第二个了 但是这样还是改变不了最不利的情况






现在说一下背包问题的二进制优化问题
其实01背包问题是最简单的背包问题 那么我们就在想能不能把完全背包问题以及多重背包问题转换为01背包问题,其实这里之间的转换就是差了个二进制优化的操作,就是把一个背包为一个每次能选择一次的新背包集合


下面先说说关于多重背包的二进制优化(个人觉得这个比较容易理解)
假设有一种大米只有M袋 例如M为100 利用二进制来分解 


 for(int j=1;j<=a[i].num;j<<=1){
                //左移一位等价于乘以  2
                weigh[coun]=j*a[i].weight;
                size[coun++]=j*a[i].value;
                a[i].num-=j;
            }
当a[i].num不为0的时候就 
weigh[coun]=a[i].num*a[i].weight;
size[coun++]=a[i].num*a[i].value;
100=1+2+4+8+16+32+37
本来正常的思路是把100弄成100个1 即100个只能使用一次的物品 但是现在把100弄成1+2+4+8+16+32+37  7个数可以组合成100以内的任意一个数 所以可以这样拆分 把每一个背包都拆成一个集合之后 在进行01背包的处理
  




for(int i=0;i<n;i++){
            scanf("%d%d%d",&a[i].value,&a[i].weight,&a[i].num);
//---------------------下面在输入后进行分解------------------------------//
            for(int j=1;j<=a[i].num;j<<=1){
                //左移一位等价于乘以  2
                weigh[coun]=j*a[i].weight;
                size[coun++]=j*a[i].value;
                a[i].num-=j;
            }
//-----------------------处理剩余的部分----------------------------------//
            if(a[i].num>0){
                weigh[coun]=a[i].num*a[i].weight;
                size[coun++]=a[i].num*a[i].value;
            }
        }


//--------------------0  1  背包问题求解--------------------------------//
        memset(dp,0,sizeof(dp));
        for(int i=0;i<coun;i++)
            for(int j=cash;j>=size[i];j--)
                dp[j]=max(dp[j],dp[j-size[i]]+weigh[i]);




好了现在来说一下完全背包问题的二进制优化问题吧 因为完全背包问题每一种背包都是没有数目的要求的。
所以你只需要你所弄的背包集合的空间集合是小于总集合的 
当跳出第一循环的时候 应该补齐剩下的(剩下的空间除以自己空间剩余的个数)


其实还是有一种更加方便的思路的 就是用一个数组记录每一种背包最多可以存在的数目 然后就可以把它视为
限制的个数 然后就可以像多重背包那样处理

猜你喜欢

转载自blog.csdn.net/hnust_lizeming/article/details/76620720