01背包问题

动态规划算法原理:先解决子问题,再逐步解决大问题

01背包问题

假设你是个小偷,背着一个可以装4磅东西的背包,你可盗窃的商品有如下三件:

商品

价格

重量

音箱

3000美元

4磅

笔记本电脑

2000美元

3磅

吉他

1500美元

1磅

为了让盗窃的商品价值最高,你选择那些商品?

每个动态规划算法从一个网格开始,背包的网格如下

背包容量/商品

1

2

3

4

吉他

音箱

笔记本

(1)先看第一行,第一个单元格表示背包的容量为1磅,吉他的重量也是1磅,这意味着他能装入背包,因此这个单元格包含吉他,价值为1500美元,下一个单元格的背包容量为2磅,完全可以装下吉他。这行的其他单元格也一样,别忘了,这是第一行,只有吉他供你选择,假设你现在还没法盗取其他两间商品。

背包容量/商品

1

2

3

4

吉他

1500

1500

1500

1500

音箱

笔记本

此时你有可能会很疑惑:原来的问题说的是4磅的背包,我们为什么要考虑容量为1磅,2磅等的背包呢?前面说过,动态规划从小问题着手,逐步解决大问题。

(2)我们来填充第二行,现在可以偷的商品有吉他和音箱。在每一行,可以偷得商品都为当前行的商品以及之前各行的商品。

该不该偷音箱呢?

背包容量为1磅,装不了音箱,所以最大价值依旧是1500美元,同理,2磅和3磅的背包也装不下音箱,因此最大价值保持不变。背包容量为4磅,可以装下音箱,此时最大价值就为3000美元了。

背包容量/商品

1

2

3

4

吉他

1500

1500

1500

1500

音箱

1500

1500

1500

3000

笔记本

(3) 下面以同样的方式处理第三行,笔记本电脑重3磅,没办法装入容量为1磅或2磅的背包,因此前两个单元格的最大价值还是1500美元。对于容量为3磅的背包,我们可以装笔记本电脑而不是吉他,此时的最大价值就可以更新为2000美元。

对于容量为4磅的背包,情况很有趣。

当前的最大价值为3000美元,你可以不偷音箱,而偷笔记本,但他只值2000美元,价格没有原来的高。但是,笔记本电脑的重量只有3磅,背包还剩下1磅没有用,此时我们想象,什么商品的重量刚好为1磅呢? 根据之前计算的最大值可以知道,1磅刚好可以装下吉他,价值1500美元。笔记本和吉他的总价值为3500美元,因此偷他们是最好的选择。

为什么计算小背包可装入商品的最大价值呢?

余下了空间时,可根据这些子问题的答案来确定余下的空间可以装入哪些商品。

背包容量/商品

1

2

3

4

吉他

1500

1500

1500

1500

音箱

1500

1500

1500

3000

笔记本

1500

1500

2000

3500

计算每一个单元格的价值时,我们可以使用如下公式:

bag[ i ] [ j ]=两者中较大的那个

1.上一个单元格的值(即 bag[ i-1 ] [ j ]的值)

vs

2.当前商品的价值 + 剩余空间的价值 ( bag[ i-1 ] [ j- 当前商品的重量 ] )

动态规划可以帮助你在给定的约束条件下找到最优解。在背包问题中,你必须在背包容量给定的情况下,偷到价值最高的商品。

在问题可以分解为彼此独立的且离散的子问题时,就可使用动态规划来解决。

最少硬币问题


题目描述

设有n 种不同面值的硬币,各硬币的面值存于数组T[1:n]中。现要用这些面值的硬币来找钱。可以使用的各种面值的硬币个数存于数组Coins[1:n]中。对任意钱数0≤m≤20001,设计一个用最少硬币找钱m的方法。

输入

输入的第一行中只有1 个整数给出n的值,第2 行起每行2 个数,分别是T[j]和Coins[j]。最后1 行是要找的钱数m。

输出

程序运行结束时,将计算出的最少硬币数输出。问题无解时输出-1。

样例输入 

3 1 3 2 3 5 3 18

样例输出 

5

提示

多组输入

思路:

动态迁移方程 a[ k ]=min( a[ k-t[ i ] ] + 1,a[ k ])

将第i个硬币拿出去得到的一个最少硬币数+1,和原硬币数相比最小的那个就是结果。

 代码

#include<stdio.h>
int min(int x,int y)
{
    return x<y?x:y;
}
int main()

{
    int n,m;
    int i,j,k;
     int t[100];
    int coin[100];
    while(~scanf("%d",&n)) //有几种不同的面值硬币

   {

    for(i=1;i<=n;i++)
        scanf("%d%d",&t[i],&coin[i]);
    scanf("%d",&m);
    int a[200002]={0}; //用来记录钱数量为i时候的最少硬币数量
    for(i=1;i<=m;i++)
        a[i]=10000;
    for(i=1;i<=n;i++) //面值硬币的种类数
        for(j=1;j<=coin[i];j++)   //面值硬币的个数
        for(k=m;k>=t[i];k--)
    {
        a[k]=min(a[k-t[i]]+1,a[k]);
    }
    if(a[m]==10000) printf("-1\n");
    else printf("%d\n",a[m]);
   }
    return 0;
}

先暂时写01背包问题,多重背包和完全背包问题我之后添加。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值