1293:买书
【题目描述】
小明手里有n元钱全部用来买书,书的价格为10元,20元,50元,100元。
问小明有多少种买书方案?(每种书可购买多本)
【输入】
一个整数 n,代表总共钱数。(0≤n≤1000)
【输出】
一个整数,代表选择方案种数。
【输入样例】
20
【输出样例】
2
【提示】
样例输入
样例输入2:
15
样例输入3:
0
样例输出
样例输出2:
0
样例输出3:
0
思路:
- 可以使用回溯搜索的方式(暴力),就是每一种书从0本到能够买的最大值都搜一下,但是算法复杂度很高。所以这里采用动态规划的方法。
- 可以看做这是一道完全背包问题。代价就是书的价格,价值就是方案数,此处的方案数需要将这个价格j能够买书此种书的最大数K,令k=0~K,依次将a[i-1][j-k*w[i]]加起来=a[i][j]。
- 呃,就是在考虑用j元买前i种书的时候可以分解成,( 买0本 i 种书,用j元考虑买前 i-1 种书的策略 )+(买1本 i 种书,用
j-w[i]元考虑买前 i-1 种书的策略)+……(直到考虑到 j 元买最大数量的 i 种书)。
二维数组中一个单元格的意义在于用 j 元考虑买前 i 种书的方案数。
注意:
1、特别要注意初始化,考虑一开始,对于第一个物体(第一种书)而言,此题不再是价格大于一本书的价格,二维表格中的数a[i][j]就有值了,而是一定能够正好买到这本书,所以应该j是w[1]的倍数,才有值,也就是一种方案。此外,从第二种书开始,如果j正好能够买到这本书,这也是要算一种方案的,所以应该将a[i-1][0]值为1。
2、最值思想体现在最多的方案数,所以利用二维数组表格将所有的情况都记录下来,每个值求的时候都要选择合适的加起来。因为涉及到要叠加,所以初始化为0( j!=0 ),且压缩只有一位数组记录是不可以的,用两个一维数组或者是采用整个二维数组也行。
(3、另:因为这是一种完全背包的求方案种数的问题,所以也可以直接将f[0][0] 初始化为1,其余初始化为0(恰好)然后从头前往后遍历就行了。emmm,联系后面几篇博客的讲解之后。)
公式:
图:例如有20元
代码:
#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
//完全背包问题 变式 还是利用那个二维数组
//书的价格就是代价 书价值是方案数
int main()
{
int a[5][1001];//代价最大是1000,数组记录方案总数 要记录上一行的内容 因为 设计到要从0累加
int w[5]={0,10,20,50,100};
memset(a,0,sizeof(a));//先将所有初始化为0
int n;//n代表总钱数
cin>>n;
int k;
a[1][0]=1;//如果后面有新加入的书 j正好是w[i]的倍数,可以全用后面品种买下 这也算是一种方案
a[2][0]=1;
a[3][0]=1;
a[4][0]=1;
for(int i=1;i<=4;i++)//4种书都走一遍 一种一种往里加 一种一种考虑
{
if(i==1)
{
for(int j=n;j>=w[i];j--)//看一下包的容量j能否装下,要能正好装下 且方案为1
{
if(j%w[i]==0)
a[i][j]=1;
}
}
else
{
for(int j=1;j<=n;j++)//j<w[i]时不用变,跟考虑到上种书a[i-1]的数值方案一样 用二维的数组记录
{
if(j<w[i])//买不下这种书
a[i][j]=a[i-1][j];
else
{
k=j/w[i];//看一下包的容量j能够装下几个物体i 依次将买这种书0本~k本的总方案都加起来,就是考虑要是可以买这种书的方案数
for(int p=0;p<=k;p++)
{
a[i][j]+=a[i-1][j-p*w[i]];
}
}
}
}
//检测
/*for(int j=0;j<=n;j++)
cout<<a[i][j]<<' ';
cout<<endl;*/
}
cout<<a[4][n];
return 0;
}