AcWing 5. 多重背包问题 II(转化为 01 背包问题 二进制优化)

题目链接:点击这里
在这里插入图片描述
在这里插入图片描述
多重背包问题描述:有 N N 种物品和一个容量为 V V 的背包。第 i i 种物品最多有 M i M_i 件可用,每件耗费的空间是 C i C_i ,价值是 W i W_i 。求解将哪些物品装入背包可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大。

一种好想好写的基本方法是转化为 01 背包求解:把第 i i 种物品换成 M i M_i 件 01 背包中的物品,则得到了物品数为 Σ M i ΣM_i 的 01 背包问题。直接求解之,复杂度仍然是 O ( N V Σ M i ) O(NV ΣM_i)

但是我们期望将它转化为 01 背包问题之后,能够像完全背包一样降低复杂度。

仍然考虑二进制的思想,我们考虑把第 i i 种物品换成若干件物品,使得原问题中第 i i 种物品可取的每种策略——取 0... M i 0 . . . M_i 件——均能等价于取若干件代换以后的物品。另外,取超过 M i M_i 件的策略必不能出现。

方法是:将第 i i 种物品分成若干件 01 背包中的物品,其中每件物品有一个系数。这件物品的费用和价值均是原来的费用和价值乘以这个系数。令这些系数分别为 1 , 2 , 2 2 . . . 2 k 1 , M i 2 k + 1 1, 2, 2^2 . . . 2^{k−1}, M_i − 2^k + 1 ,且 k k 是满足 M i 2 k + 1 > 0 M_i − 2^k + 1 > 0 的最大整数。

例如,如果 M i M_i 13 13 ,则相应的 k = 3 k = 3 ,这种最多取 13 13 件的物品应被分成系数分别为 1 , 2 , 4 , 6 1, 2, 4, 6 的四件物品。

分成的这几件物品的系数和为 M i M_i ,表明不可能取多于 M i M_i 件的第 i i 种物品。另外这种方法也能保证对于 0... M i 0 . . . M_i 间的每一个整数,均可以用若干个系数的和表示。(算法正确性的证明可以分 0... 2 k 1 0 . . . 2^{k−1} 2 k . . . M i 2^k . . . M_i 两段来分别讨论得出,感兴趣的可以尝试一下)

这样就将第 i i 种物品分成了 O ( l o g M i ) O(logM_i) 种物品,将原问题转化为了复杂度为 O ( N V Σ l o g M i ) O(NV ΣlogM_i) 的 01 背包问题,是很大的改进。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>

using namespace std;

const int maxn = 2010;

int dp[maxn];

struct Good {
	int v, w;
};

int main()
{
	vector<Good> goods;
    int N, V;
    scanf("%d%d", &N, &V);
    
    for(int i = 0; i < N; ++i)
    {
    	int v, w, s;			        //v物品体积,w物品价值,s物品数量 
    	scanf("%d%d%d", &v, &w, &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});
	}
    
    
	for(auto it : goods)
    {
    	for(int j = V; j >= it.v; --j)
    	{
    		dp[j] = max(dp[j], dp[j-it.v]+it.w);    		
		}
	}
    
    
    printf("%d", dp[V]);
    
    return 0;
}
发布了748 篇原创文章 · 获赞 113 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/qq_42815188/article/details/104364405