hdoj 2191 多重背包入门题

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2191

该题目为裸的多重背包 入门题。。纯粹用来检测自己对背包九讲的理解。。

结果WA了居然因为忘记了多组数据的循环语句。。果然太久没写,写的越来越渣。。

此处采用的第一种方法为转化为01背包问题,并没有进行优化。。

状态转移方程为F[i ,v] = max {F[i − 1,v − k ∗ C i ] + k ∗ W i | 0 ≤ k ≤ M i }

然后我们依然可以转化为一维数组来进行求解。。。

此处附上该方法的代码:

#include<iostream>
#include<cstring>
using namespace std;
int c[110];
int w[110];
int m[110];
int dp[110];
int max(int a,int b)
{
	return (a>b)?a:b;
}
int main ()
{
	int t,n,V;
	cin >> t;
	while(t--)
	{
		memset(dp,0,sizeof(dp));		
		cin >> V >> n;
		for(int i = 1;i <= n;i++)
		cin >> c[i] >> w[i] >> m[i];
		for(int i = 1;i <= n;i++)
		{
			for(int j = V;j >= c[i];j--)
			{
				for(int k = 1;k <= m[i] && k*c[i] <= j;k++)  //保证 k不超过该物品的个数,且花费不能超过经费 
					dp[j] = max(dp[j],dp[j-k*c[i]]+k*w[i]);
			}
		}
		cout << dp[V] << endl;
	}
	return 0;
}
然后一种方法是转化成二进制的方法。背包九讲中时这样说的:

仍然考虑二进制的思想,我们考虑把第 i 种物品换成若干件物品,使得原问
题中第 i 种物品可取的每种策略——取 0...M i 件——均能等价于取若干件代换
以后的物品。另外,取超过 M i 件的策略必不能出现。
方法是:将第 i 种物品分成若干件01背包中的物品,其中每件物品有一个系
数。这件物品的费用和价值均是原来的费用和价值乘以这个系数。令这些系数
分别为 1,2^2,2^3 , ...2 k−1 ,M i − 2 k + 1 ,且 k 是满足 M i − 2 k + 1 > 0 的最大整数。例
如,如果 M i 为 13 ,则相应的 k = 3 ,这种最多取 13 件的物品应被分成系数分别
为 1,2,4,6 的四件物品。
分成的这几件物品的系数和为 M i ,表明不可能取多于 M i 件的第 i 种物品。另
外这种方法也能保证对于 0...M i 间的每一个整数,均可以用若干个系数的和表
示。这里算法正确性的证明可以分 0...2 k−1 和 2 k ...M i 两段来分别讨论得出,
希望读者自己思考尝试一下。

伪代码如下:

def MultiplePack( F , C , W , M )
if C · M ≥ V
CompletePack( F , C , W )
return
k := 1
while k < M
ZeroOnePack( kC , kW )
M := M − k
k := 2k
ZeroOnePack( C · M , W · M )

思考了一下感觉伪代码中的拆分方式并不是跟前文的一样,,伪代码中是将背包中的拆分成 1,2^2,2^3,....2^k, m - 2^k,   且k是满足m - 2^k > 0 的最大整数。。

这样不管拿几个该物品也可以将所有方式都表示出来,所以应该是一样的。。

于是将该方法实现了一下。。

代码如下:

#include<iostream>
#include<cstring>
using namespace std;
int w[110];
int c[110];
int m[110];
int dp[110];
int max(int a,int b)
{
	return (a>b) ? a:b;
}
int main ()
{
	int t,n,V;
	cin >> t;
	while (t--)
	{
		memset(dp,0,sizeof(dp));		
		cin >> V >> n;
		for(int i = 1;i <= n;i++)
		cin >> c[i] >> w[i] >> m[i];
		for(int i = 1;i <= n;i++)
		{
			if(c[i]*m[i] >= V)
			{
				for(int j = c[i];j <= V;j++)
					dp[j] = max(dp[j],dp[j-c[i]]+w[i]); 
			}
			else 
			{
				int k = 1;
				while (k < m[i])
				{
					for(int j = V;j >= k*c[i];j--)
						dp[j] = max(dp[j],dp[j-k*c[i]]+k*w[i]);
					m[i] = m[i] - k;
					k = 2*k;
				}
				for(int j = V;j >= c[i]*m[i];j--)  //此为拆到最后的那个 m - 2^k的物品 
					dp[j] = max(dp[j],dp[j-m[i]*c[i]] + m[i]*w[i]); 
			}
		}
		cout << dp[V] << endl;
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值