PAT甲级 1068

1068 Find More Coins

1.回溯法

将数据从小到大进行排序,然后依次考虑有各个物品的情况,逻辑如图,是一个深度遍历该树的过程。(左分支为选取某个物品,右分支为不选取)

在这里插入图片描述
这种方法效率低,尤其是在没有答案的时候,要遍历完整棵树才能得到结果,最差时间复杂度为O(2^n)。

2.动态规划

本题也可以理解为01背包问题(背包容量就是要求的数总和物品个数就是数的个数,每件物品价值等于物品重量,所以背包价值等于背包重量),用动态规划解决。而在使用动态规划解题,有如下几个注意的点:

1)f [m][n]的求解

在01背包问题,设背包容量为9,物品总数为8,各物品总量为 w[i] 。约定一个数组f[8+1][9+1],数组f[m][n]存放对于前m个物品,背包容量为n时物品总价值的最大值。f[m][n] = max { f[m-1][n],f[m-1][n-w[i]] },我们目标是求 f[8][9] (下标从1开始),根据递推公式很容易就有了自顶向下的求法,如下图。在这里插入图片描述
这种方法没有缩小时间复杂度还是O(2^n),但是如果换个思路,自底向上,如图。

(1,1)(1,2)(1,3)(1,9)
(2,1)(2,2)(2,3)(2,9)
(3,1)(3,2)(3,3)(3,9)
(8,1)(8,2)(8,3)(8,9)

从第一行出发从左到右,从下至下求得整个数组,或者从列出发也是可以的,从而实现将时间复杂度缩小到O(m*n)。

2)进一步得缩小空间复杂度

前面我们使用二维数组 f[m][n] 来保存信息,实际上还可以压缩成一维数组。
在这里插入图片描述
我们观察可以发现,求取数组中某个位置的值,只和前一层数据有关(不是这一层,不是前两层,前三层等等,仅仅前一层)。所以我们依旧是从上到下求解这个数组,但是每层从后往前求。
假如,设数组 g[9+1],求解原来的 f[2][9],放置于 g[9]。f[2][9]仅需要 f[1][1-8]
,而g[1-8]刚好存放的就是这些值。这就是为什么每层倒序求的原因,能保证所求位置前面的数据都是上层数据,而求取的时候也只使用到该位置以前的数据

2)是否有解,以及求取最小解

求取出了g[n],如果 g[n]==n 那么就是有解,否则无解。
而关于最小解,首先,我们需要将所有物品(数字)倒序排列。其次,我们需要设置一个 初值均为 false 的choose[m][n] 数组。如果遍历到物品 i,重量 j 时,g[j]更新了,那么 choose[i][j]=true,即可以理解为对于重量 j的所有解,物品 i 可以是其中一个解的一员。 我们可以利用choose[m][n]把解拆出来,拆分从最后一个物品开拆,那么我们前面倒序排列的目的就很明确了,就是为了优先把重量小的物品拆出来。
最后,我们按如下规则遍历choose数组,就能得到最小解。

int v = n, index = m;
//v初值为n是因为求的是总和为n的解,index初值为m是因为优先考虑编号
while(v > 0) {
    if(choose[index][v] == true) {//如果是解的成员,拆出来
        arr.push_back(w[index]);
        v -= w[index];//改变重量
    }
    index--;//改变可拆物品范围
}
参考链接:
  1. 01背包详解
  2. 动态规划详解
  3. 1068. Find More Coins (30)-PAT甲级真题(01背包)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值