【算法打卡(完全背包)--7.29】

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

完全背包:
动态转移方程满足:dp[i][j]=max{dp[i-1][ j-k×v[i] ] + k×w[i]},其中 0 ≤ k×v[i] ≤ j
可以发现,当k只能取0、1时的特例就是简单的0-1背包问题。

可以发现,选0次,即不选是肯定存在的,dp[i][j]=dp[i-1][j];
然后选1次,那就不一定存在了,可能可以选,也可能不可以选,假设可以选,在选0次该物品求出dp[i][j]的基础上,确定是否选该物品:if (j>=v[i]) dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
选2次、选3次,依次类推,一直到选k次。


提示:以下是本篇文章正文内容,下面案例可供参考

一、 零钱兑换

public class Solution {
    /**
     * leetcode: 优化dp,用max而不是Integer.MAX_VALUE(因为int最大值+1 会溢出)
     * f[i]表示凑成i需要的次数
     */
    public int coinChange(int[] coins, int amount) {
        int max = amount + 1;
        int[] f = new int[amount + 1];
        Arrays.sort(coins);
        Arrays.fill(f, max);
        f[0] = 0;
        for (int i = 1; i <= amount; i++) {
            //从后往前取,每次取到的都是最大的coin,次数最少
            for (int j = 0; j < coins.length &&  coins[j] <= i; j++) {
                f[i] = Math.min(f[i], f[i - coins[j]] + 1);
            }
        }
        return f[amount] == max ? -1 : f[amount];
    }

}

 

在这里插入图片描述

二、 零钱兑换||

同时硬币相当于我们的物品,每种硬币可以选择「无限次」,很自然的想到「完全背包」。
这时候可以将「完全背包」的状态定义搬过来进行“微调”:
定义 f[i][j] 为考虑前 i 件物品,凑成总和为 j 的方案数量。
为了方便初始化,我们一般让 f[0][x] 代表不考虑任何物品的情况。
因此我们有显而易见的初始化条件:f[0][0] = 1,其余 f[0][x] = 0。
代表当没有任何硬币的时候,存在凑成总和为 0 的方案数量为 1;凑成其他总和的方案不存在。

当「状态定义」与「基本初始化」有了之后,我们不失一般性的考虑 f[i][j]f[i][j] 该如何转移。
在二维解法的基础上,直接取消「物品维度」
确保「容量维度」的遍历顺序为「从小到大」(适用于「完全背包」)
将形如 f[i - 1][j - k * val] 的式子更替为 f[j - val] ,同时解决「数组越界」问题

class Solution {
    public int change(int cnt, int[] cs) {
        int n = cs.length;
        int[] f = new int[cnt + 1];
        f[0] = 1;
        for (int i = 1; i <= n; i++) {
            int val = cs[i - 1];
            for (int j = val; j <= cnt; j++) {
                f[j] += f[j - val];
            }
        }
        return f[cnt];
    }
}

在这里插入图片描述

三、数位成本和为目标值的最大数字

状态表示 :f[i][j] :前i项中任意选,且总代价恰好是j的最大数字为 f
状态计算 :f[i][j] = max_s(f[i - 1][j] , to_string(i) + f[i - 1][j - cost[i - 1]] );
上面和一般的完全背包没有太大的区别,主要是i为什么要放在前面?
答:是因为我们是从小到大枚举下标的,那么越往后下标越大,越大的数,位数越前,那整体肯定就越大 。

class Solution {
public:
    string f[5005]   ;
    string largestNumber(vector<int>& cost, int t) {
        fill_n(f,t + 1,"0");    // 0表示不合法
        f[0] = "1";     // 只有从前0个中选0个合法 为 1
        for(int i = 1;i <= 9 ; ++ i)        // 完全背包模型
            for(int j = cost[i - 1];j <= t; ++ j)
                f[j] = max_s(f[j] , to_string(i) + f[j - cost[i - 1]] );

        if(f[t].back() == '0') return "0";  // 从0过来的 都不合法
        return f[t].substr(0,f[t].size() - 1);  // 去掉1

    }
    string max_s  (const string & a,const string & b){ 
            if(a.back() == '0') return b; // 不合法的都是最小的
            if(b.back() == '0') return a;
            if(a.size() != b.size()) return a.size() > b.size() ? a:b;
            return max(a,b);
        }
};

 

在这里插入图片描述

总结

南无阿你拖佛!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

人间凡尔赛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值