题意:输入第一行cash N n1 D1 n2 D2 ... nN DN ,D1表示一种面值的零钱,n1表示D1可以使用的数量。一共有N种零钱。需要找出用这些零钱可以组成的不超过cash的最大数额。
这是一道多重背包的问题,和poj-1014 多重背包问题类似,需要先转化成01背包,再用01背包问题的方法求解。转换的代码如下,a[i]是转换前第i个零钱的数量,b[i]是转换前第i个零钱的面值,w[count]是转换后的第count个零钱的面值,转换后每种零钱只有一个,但是可以组合成转换前的各种组合。
int count = 1;
for(int i=1;i<=n;i++)
{
int c=1;
while(a[i]-c>0)
{
w[count++] = c*b[i];
a[i] -= c;
c = c << 1;
}
w[count++] = a[i]*b[i];
}
之后用01背包的方法计算,因为没有背包问题中体积这一参数,可以将体积设置为与价格一致。状态转移方程为dp[j] = max(dp[j],dp[j-w[i]]+w[i]),用一维数组来表示需要从后往前变化,每一行已经扫描到的部分属于第i行,未扫描到的部分属于第i-1行,j表示第j列。代码如下:
for( i=1;i<count;i++)
{
for(int j=all;j>=w[i];j--)
{
dp[j] = max(dp[j],dp[j-w[i]]+w[i]);
}
}
完整的AC代码如下:
#include<iostream>
#include<cstring>
#include<math.h>
using namespace std;
int all,n;
int a[210],b[210],w[210],dp[110000];
void dp_fun()
{
//多重变换为01背包
memset(dp,0,sizeof(dp));
int count = 1;
for(int i=1;i<=n;i++)
{
int c=1;
while(a[i]-c>0)
{
w[count++] = c*b[i];
a[i] -= c;
c = c << 1;
}
w[count++] = a[i]*b[i];
}
int i;
for( i=1;i<count;i++)
{
for(int j=all;j>=w[i];j--)
{
dp[j] = max(dp[j],dp[j-w[i]]+w[i]);
}
}
cout << dp[all]<<endl;
}
int main()
{
while(cin >> all)
{
cin >> n;
for(int i=1;i<=n;i++)
{
cin >> a[i] >> b[i];
}
dp_fun();
}
return 0;
}