POJ 1742 多重背包问题

39 篇文章 0 订阅

在《背包九读》里,作者提到了最后的一种基本背包问题是多重背包问题

在所有背包问题中,问题都是一致的:有若干个物体,P(Wi, Vi) ,每个重量用wi表示,选择后能获得的价值用Vi表示。然后有一个总容量,在满足不超过总容量的情况下,使得选择的物体尽量价值最大。

01背包中,物体只有一个要么选,要么不选。

在 完全背包问题中,每个物体都有无数个,也可以选择无数个。

在 多重背包中,物体的个数是受限的,用P(Wi, Vi, Ai)表示。最大可以选择Ai个。


在《背包九读》中,作者给出一种思路是将多重背包问题,拆解成01背包,拆解的原则是将物体分解。

假如我们有13个物体,那么如何分解成一组整数,使得他们的组合可以表示出 1 - 13中的任意一个呢,作者给出的思路是利用二进制的分解方法,将一个整数分解出 1 2 4 ...等因子。

但是最后一个因子是有学问的,因为我们不能使得分解出来的所有数的和大于Ai,所以最后一个数是 Ai - (1+2+...2^(k-1)) = Ai - (2^k - 1) > 0

所以上面的解就是将之分解为 1 2 4 6 这样的基。 然后将 1倍的物体i理解成新物体,2倍的物体i理解成一个新的物体.....

复杂度降低到 O(N * M * log(A)).


不过这个算法看起来似乎仍然没有那么漂亮。没有aha的感觉吧

楼教主的思路貌似不怎么好理解吧,感觉网上广泛流行的是使用如下的手段来做转换:从完全背包转换成多重背包问题。

当在处理 第 i 中硬币的时候,我们记录 获得每一个金钱值 所需要的 当前硬币的数量,如果这个数量满足条件(小于等于给定的数目),我们才会选择这中硬币,否则不会。

用一个数组,我们将完全背包问题加以限制,就实现了一个多重背包。

代码如下:


#include <stdio.h>
#include <string.h>
#define COIN_NUM 105
#define MONEY_SUM 100005

int coin[COIN_NUM];
int amount[COIN_NUM];
int dp[MONEY_SUM];
int used[MONEY_SUM];

int nCoin, nSum;

int GetNumOfPayments()
{
	memset(dp, 0, sizeof(dp));

	dp[0] = 1;

	int i ,j;
	for(i = 0; i < nCoin; i++)
	{
		memset(used, 0, sizeof(used));

		for( j = coin[i]; j <= nSum; j++)
		{
            //因为这个问题仅仅是 求出是否即可,所以用bool就可以实现
			if(dp[j-coin[i]] && !dp[j] && used[j-coin[i]] + 1 <= amount[i])
			{
				dp[j] = 1;
				used[j] = used[j-coin[i]] + 1;
			}
		}
	}
	int nTotal = 0;
	for( i = 1; i <= nSum; i++)
	{
		nTotal += dp[i];

	}
	return nTotal;
}


int main()
{
	int i,j;
	while(scanf("%d%d", &nCoin, &nSum) != EOF)
	{
		if(nCoin == 0 && nSum == 0)
		{
			break;
		}

		for(i = 0; i <  nCoin; i++)
		{
			scanf("%d", coin + i);
		}
		for( i = 0; i < nCoin; i++)
		{
			scanf("%d", amount + i);
		}
		
		printf("%d\n",GetNumOfPayments());

	}
	return 0;
}

代码的复杂度是 O(N*M)应该是一个比较不错的解法了~

总之来说今天收获还是很大的,弄清楚了几个基本类型的背包问题,AC了四个相关练习,为接下来找工作又多了一份自信。

准备明天挑战FB的 Online Puzzle,好运~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值