动态规划一日一题-10-8-混合背包问题


混合背包问题


有 N种物品和一个容量是 V 的背包。

物品一共有三类:

  • 第一类物品只能用1次(01背包);
  • 第二类物品可以用无限次(完全背包);
  • 第三类物品最多只能用 si次(多重背包);

每种体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

  • si=−1 表示第 i种物品只能用1次;
  • si=0 表示第 ii 种物品可以用无限次;
  • si>0表示第 i 种物品可以使用 si 次;

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤1000
0<vi,wi≤1000
−1≤si≤1000

输入样例

4 5
1 2 -1
2 4 1
3 4 0
4 5 2

输出样例:

8

其实从大体上来看,背包问题其实只有01,完全两大问题。对于混合背包,分组背包,多重背包其实就是通过某种优化手段,将其映射到01和完全两大背包类型中。

例如,此题的混合背包问题,昨天我们做了多重背包问题,不同体积与价值的物品可以有不同的数量。我们采用了一种二进制优化的方法,将多重背包映射成为01背包,然后套用01的模板即可。

好的 ,那么我们开始举一反三,混合背包可以简化成什么问题。

我们从数量开始入手,对于有固定容积的背包来说,其实是不存在无穷这一概念的,也就是说物品的无穷相对于优先的背包容积来说,是不存在的。本质上,全是多重背包问题。

ok,明确了这一点后,我们就可以把此题当做多重背包来做,然后可以用二进制优化的方法进行转化成01背包。

代码如下:

#include 

using namespace std ;

const int N = 10010 ; 

int f[N] ; 
int w[N] , v[N] ;

int main()
{
    int n , m ;  
    
    cin >> n >> m ; 
    
    int cnt = 0 ; 
    
    for(int i = 1 ; i <= n ; ++i)
    {
        int a , b , c ; 
        
        cin >> a >> b >> c ; 
        if(c == -1)
        {
            cnt ++ ;
            v[cnt] = a  ; 
            w[cnt] = b ; 
        }
        else if(c == 0)
        {
            c = m / a ;
            int k = 1 ;
            while(c >= k )
            {
                cnt ++ ;
                v[cnt] = a * k ; 
                w[cnt] = b * k ;
                c -= k ; 
                k *= 2  ;
            }
            if (c)
            {
                cnt ++ ;
                v[cnt] = a * c; 
                w[cnt] = b * c  ;
            }
        }
        else 
        {
            int k = 1 ; 
            while(c >= k)
            {
                cnt ++ ;
                v[cnt] = a * k ; 
                w[cnt] = b * k ;
                c -= k ; 
                k *= 2  ;
            }
            if(c)
            {
                cnt ++ ; 
                v[cnt] = a * c ; 
                w[cnt] = b * c ; 
            }
        }
    }
    n = cnt ; 
    for(int i = 1 ; i <= n ; ++i)
    {
        for(int j = m ; j >= v[i] ; --j)
        {
            f[j] = max(f[j] , f[j-v[i]] + w[i]) ;
        }
    }
    cout << f[m] << endl ;
    return  0 ; 
}

文章作者: 罗林
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 罗林 !
  目录