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