完全背包变式 一本通 1293:买书

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;
 } 
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值