HDU - 2844 Coin(多重背包转化为01背包)

题目链接:Coin

题意: Whuacmers拥有bi个面值为ai的硬币,现在他要用这些硬币买价格不超过m的一个物品,问你最多能刚好能用硬币付钱的物品价格有几个(即该价格能用这些硬币凑出来)。

思路: 看到多重背包问题,第一时间想到的是转化为01背包来做,即我们把这个物品能选取多次当成有多个相同的物品给我们选取,复杂度是o(m*(bi的和)),根据题目给出的数据范围,这个方法的复杂度是妥妥的TLE的,我们需要对这个方法进行优化,我们可以用二进制的思想来考虑.

将第i种物品分成若干件01背包中的物品,其中每件物品有一个系数。这件物品的费用和价值均是原来的费用和价值乘以这个系数。令这些系数 分别为1,2,22…2k−1,bi−2k+1,且k是满足bi−2k+1 > 0的最大整数。例 如,如果bi为13,则相应的k = 3,这种最多取13件的物品应被分成系数分别 为1,2,4,6的四件物品。 分成的这几件物品的系数和为bi,表明不可能取多于bi件的第i种物品。另 外这种方法也能保证对于0…bi间的每一个整数,均可以用若干个系数的和表示。

通过上述方法,我们将转化为01背包的物品数量大大减小,这道题也迎刃而解了.

AC代码


using namespace std;
int n,m,k;
int w[110],v[110];
long long bag[300010];

int main() {
	while(cin>>n>>m&&n!=0&&m!=0){
		bag[0]=1; 
		for(int i=0;i<n;i++) cin>>w[i];
		for(int i=0;i<n;i++) cin>>v[i];
		for(int i=0;i<n;i++) {
			int k = 1;
			do{
				if(k<v[i]) {
					v[i]-=k;
				}
				else {
					k=v[i];
					v[i]=0;
				}
				for(int j=m-k*w[i];j>=0;j--) {
					if(bag[j]==0) continue;
					bag[j+k*w[i]] = 1;
				}
				k*=2;
			}while(v[i]>0);
		}
		int ans = 0;
		for(int i=1;i<=m;i++) {
			if(bag[i]==0) continue;
			ans++;
			bag[i]=0;
		}
		cout<<ans<<endl;
	}
	return 0;
}

对背包问题不了解的同学可以百度搜索‘背包九讲’。

不懂的欢迎在评论区留言。

猜你喜欢

转载自blog.csdn.net/qq_43305984/article/details/88544707