HDU2191 多重背包 三种解法

Problem Address:http://acm.hdu.edu.cn/showproblem.php?pid=2191


【思路】


简单的多重背包。

但是多重背包并不简单。以下简单介绍多重背包的三种解法。


第一种:O(V*Σ(Ki))

这就是最平常的解法。把多重背包当做01背包来做。

把每一件物品的k个数量拆分为k个物品,然后进行01背包。


第二种:O(V*∑([log2Ki]))

这是前一个方法的升级。利用了二进制的思想。

把k拆分为1、2、4……2^(t-1)、Ki-2^t+1的数列,其中t是满足Ki-2^t+1>0的最大整数。

可以想象一下,比如7以内的任意数,是可以通过1、2、4这三个数组合而来。

以上两种方法在《背包九讲》中都有讲解。


第三种:O(VN)

这种方法利用了单调队列。可以先了解单调队列。

详细方法参考国家集训队论文《浅谈几类背包问题》。

达到了和01背包一样的时间复杂度。


【代码】



第一种:O(V*Σ(Ki))


#include <iostream>
using namespace std;

const int maxn = 100;

int dp[maxn+5];

int main()
{
	int t;
	int n, m, p, h, c;
	int i, j, k;
	scanf("%d", &t);
	while(t--)
	{
		memset(dp, 0, sizeof(dp));
		scanf("%d %d", &n, &m);
		for (i=0; i<m; i++)
		{
			scanf("%d %d %d", &p, &h, &c);
			for (j=n; j>=0; j--)
			{
				for (k=1; k<=c && j+k*p<=n; k++)
				{
					if (dp[j]+k*h>dp[j+k*p])
						dp[j+k*p] = dp[j] + k*h;
				}
			}
		}
		printf("%d\n", dp[n]);
	}
	return 0;
}



第二种:O(V*∑([log2Ki]))


#include <iostream>
using namespace std;

const int maxn = 100;

int dp[maxn+5];

int main()
{
	int t;
	int n, m, p, h, c;
	int i, j, k, w, val;
	scanf("%d", &t);
	while(t--)
	{
		memset(dp, 0, sizeof(dp));
		scanf("%d %d", &n, &m);
		for (i=0; i<m; i++)
		{
			scanf("%d %d %d", &p, &h, &c);
			for (k=1; c-k*2+1>0; k*=2)
			{
				w = k*p;
				val = k*h;
				for (j=n-w; j>=0; j--)
				{
					if (dp[j]+val>dp[j+w])
						dp[j+w] = dp[j]+val;
				}
			}
			k = c - k + 1;
			w = k*p;
			val = k*h;
			for (j=n-w; j>=0; j--)
			{
				if (dp[j]+val>dp[j+w])
					dp[j+w] = dp[j]+val;
			}
		}
		printf("%d\n", dp[n]);
	}
	return 0;
}



第三种:O(VN)


#include <iostream>
using namespace std;

const int maxn = 100;

int dp[maxn+5];
int A[maxn+5];
int B[maxn+5];
int L, R;

inline void insert(int a, int b)//插入一个position为a,值为b的元素到队列中
{
	while(R>=L && b>B[R]) R--;
	R++;
	A[R] = a;
	B[R] = b;
}

int main()
{
	int t;
	int n, m, p, h, c;
	int i, j;
	int d;
	scanf("%d", &t);
	while(t--)
	{
		memset(dp, 0, sizeof(dp));
		scanf("%d %d", &n, &m);
		for (i=0; i<m; i++)
		{
			scanf("%d %d %d", &p, &h, &c);
			for (d=0; d<p; d++)
			{
				L = 1;
				R = 0;
				for (j=0; j<=(n-d)/p; j++)
				{
					insert(j, dp[j*p+d]-j*h);
					if (A[L]<j-c)//如果队列的首元素已经失效
						L++;
					dp[j*p+d] = B[L] + j*h;
				}
			}
		}
		printf("%d\n", dp[n]);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值