题目链接: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; }