题意:
一家银行计划安装一台用于提取现金的机器。
机器能够按要求的现金量发送适当的账单。
机器使用正好N种不同的面额钞票,例如D_k,k = 1,2,…,N,并且对于每种面额D_k,机器都有n_k张钞票。
例如,
N = 3,
n_1 = 10,D_1 = 100,
n_2 = 4,D_2 = 50,
n_3 = 5,D_3 = 10
表示机器有10张面额为100的钞票、4张面额为50的钞票、5张面额为10的钞票。
东东在写一个 ATM 的程序,可根据具体金额请求机器交付现金。
注意,这个程序计算程序得出的最大现金少于或等于可以根据设备的可用票据供应有效交付的现金。
程序输入来自标准输入。 输入中的每个数据集代表特定交易,其格式为:Cash N n1 D1 n2 D2 … nN DN其中0 <= Cash <= 100000是所请求的现金量,0 <= N <= 10是 纸币面额的数量,0 <= nk <= 1000是Dk面额的可用纸币的数量,1 <= Dk <= 1000,k = 1,N。 输入中的数字之间可以自由出现空格。 输入数据正确。
对于每组数据,程序将在下一行中将结果打印到单独一行上的标准输出中。
思路:
题目是一个多重背包问题,n种钞票,请求现金为V,每种钞票面额为v[i]&w[i],个数为c[i]。
首先要进行二进制拆分,降低时间复杂度。对c[i]个物品进行拆分,分为多组物品,每组物品一个,且所有组物品的v[i]、w[i]的和加起来刚好为v[i]、w[i]。思路是拆分为1,2,4,8……最后还会剩下一部分,它是最后的值。比如13会分为1,2,4,此时剩下7不够8,所以最后的值为7,即13分为了1,2,4,7。
接下来就是对cnt,V,vv[],ww[]进行0-1背包。注意到请求现金V最大值为100000,题目所给内存为1000KB,要用到滚动数组来省内存。即原来的f[i][j]=max(f[i][j],f[i-1][j-w[i]]+v[i])变为f[j]=max(f[j],f[j-w[i]]+v[i]),同时j从V到0反向遍历。
总结:
一道动态规划-多重背包的题目,要用到二进制拆分和滚动数组。A掉该题后对相关思想有了进一步的理解和掌握。
代码:
#include <iostream>
using namespace std;
int n,V;
int v[20],w[20],c[20];
//多重背包问题,n种钞票,请求现金为V,每种钞票面额为v[i]&w[i],个数为c[i]
//二进制拆分为cnt种钞票,请求现金为V,每种钞票面额为vv[i]&ww[i]的01背包
int f[100010];
int vv[210],ww[210];
int cnt=0;
void chaifen()
{
cnt=0;
for(int i=1;i<=n;i++)
{
int t=c[i];
int k=1;
for(;k<=t;k<<=1)
{
cnt++;
vv[cnt]=k*v[i];
ww[cnt]=k*w[i];
t-=k;
}
if(t>0)
{
cnt++;
vv[cnt]=t*v[i];
ww[cnt]=t*w[i];
}
}
}
void solve() //对cnt,V,vv[],ww[]进行0-1背包
{
for(int i=0;i<=V;i++)
f[i]=0;
for(int i=1;i<=cnt;i++)
{
for(int j=V;j>=0;j--)
{
if(j-ww[i]>=0)
f[j]=max(f[j],f[j-ww[i]]+vv[i]);
}
}
}
int main()
{
while(cin>>V)
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>c[i]>>v[i],w[i]=v[i];
chaifen();
solve();
cout<<f[V]<<endl;
}
}
博客介绍了如何用动态规划解决一个ATM程序设计问题,该问题涉及多重背包,要求根据不同的钞票面额和数量满足现金提取请求。通过二进制拆分降低时间复杂度,并使用滚动数组优化内存,实现高效解决方案。
1465

被折叠的 条评论
为什么被折叠?



