算法奇妙屋(二十三)-完全背包问题(动态规划)

文章目录

一. 牛客 DP42 【模板】完全背包

1. 题目解析

和01背包很相似, 却又有些许不同

2. 算法原理

(1) 第一问

(2) 第二问

3. 代码

java 复制代码
import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    static Scanner in = new Scanner(System.in);
    static int n = in.nextInt();
    static int V = in.nextInt();
    public static void main(String[] args) {
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextInt()) { // 注意 while 处理多个 case
            int[] v = new int[n + 1];
            int[] w = new int[n + 1];
            for (int i = 1; i <= n; i++) {
                v[i] = in.nextInt();
                w[i] = in.nextInt();
            }
            f1(v, w);
            f2(v, w);
        }
    }
    static void f1(int[] v, int[] w) {
        // 建表
        int[][] dp = new int[n + 1][V + 1];
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= V; j++) {
                dp[i][j] = dp[i - 1][j];
                if (j >= v[i]) {
                    dp[i][j] = Math.max(dp[i][j], dp[i][j - v[i]] + w[i]);
                }
            }
        }
        System.out.println(dp[n][V]);
    }
    static void f2(int[] v, int[] w) {
        // 建表
        int[][] dp = new int[n + 1][V + 1];
        // 初始化
        for (int j = 1; j <= V; j++) {
            dp[0][j] = -1;
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= V; j++) {
                dp[i][j] = dp[i - 1][j];
                if (j >= v[i] && dp[i][j - v[i]] != -1) {
                    dp[i][j] = Math.max(dp[i][j], dp[i][j - v[i]] + w[i]);
                }
            }
        }
        System.out.println(dp[n][V] == -1 ? 0 : dp[n][V]);
    }
}

4. 优化

优化过程和01背包一样, 也是利用滚动数组进行空间上的优化

5. 优化后的代码

java 复制代码
import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    static Scanner in = new Scanner(System.in);
    static int n = in.nextInt();
    static int V = in.nextInt();
    public static void main(String[] args) {
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextInt()) { // 注意 while 处理多个 case
            int[] v = new int[n + 1];
            int[] w = new int[n + 1];
            for (int i = 1; i <= n; i++) {
                v[i] = in.nextInt();
                w[i] = in.nextInt();
            }
            f1(v, w);
            f2(v, w);
        }
    }
    static void f1(int[] v, int[] w) {
        // 建表
        int[] dp = new int[V + 1];
        for (int i = 1; i <= n; i++) {
            for (int j = v[i]; j <= V; j++) {
                dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);
            }
        }
        System.out.println(dp[V]);
    }
    static void f2(int[] v, int[] w) {
        // 建表
        int[] dp = new int[V + 1];
        // 初始化
        for (int j = 1; j <= V; j++) {
            dp[j] = -1;
        }
        for (int i = 1; i <= n; i++) {
            for (int j = v[i]; j <= V; j++) {
                if (dp[j - v[i]] != -1) {
                    dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);
                }
            }
        }
        System.out.println(dp[V] == -1 ? 0 : dp[V]);
    }
}

二. 力扣 322. 零钱兑换

1. 题目解析

当我们看到一个元素可以选任意多个时, 要试着转化为完全背包问题

2. 算法原理

要注意区别理解这里为什么不能用-1来代表数值和不能等于j的情况

3. 代码

java 复制代码
class Solution {
    public int coinChange(int[] coins, int amount) {
        // 建表
        int n = coins.length;
        int[][] dp = new int[n + 1][amount + 1];
        // 初始化
        for (int j = 1; j <= amount; j++) {
            dp[0][j] = 0x3f3f3f3f;
        }
        // 填表
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= amount; j++) {
                dp[i][j] = dp[i - 1][j];
                if (j >= coins[i - 1]) {
                    dp[i][j] = Math.min(dp[i][j], dp[i][j - coins[i - 1]] + 1);
                }
            }
        }
        // 返回结果
        return dp[n][amount] >= 0x3f3f3f3f ? -1 : dp[n][amount];
    }
}

4. 优化后的代码

删除 i 维度, j 的范围缩小, 优化的算法原理推导过程在第一题

java 复制代码
class Solution {
    public int coinChange(int[] coins, int amount) {
        // 建表
        int n = coins.length;
        int[] dp = new int[amount + 1];
        // 初始化
        for (int j = 1; j <= amount; j++) {
            dp[j] = 0x3f3f3f3f;
        }
        // 填表
        for (int i = 1; i <= n; i++) {
            for (int j = coins[i - 1]; j <= amount; j++) {
                dp[j] = Math.min(dp[j], dp[j - coins[i - 1]] + 1);
            }
        }
        // 返回结果
        return dp[amount] >= 0x3f3f3f3f ? -1 : dp[amount];
    }
}

三. 力扣 518. 零钱兑换 II

1. 题目解析

这道题和上一道题基本类似, 只是细节稍有不同

2. 算法原理

这里我们省略了选择i号硬币时的推导过程

3. 代码

java 复制代码
class Solution {
    public int change(int amount, int[] coins) {
        //建表
        int n = coins.length;
        int[][] dp = new int[n + 1][amount + 1];
        //初始化
        dp[0][0] = 1;
        //填表
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= amount; j++) {
                dp[i][j] = dp[i - 1][j];
                if (j >= coins[i - 1]) {
                    dp[i][j] = dp[i][j] + dp[i][j - coins[i - 1]];
                }
            }
        }
        //返回结果
        return dp[n][amount];
    }
}

4. 优化后的代码

删除 i 维度, j 的范围缩小, 优化的算法原理推导过程在第一题

java 复制代码
class Solution {
    public int change(int amount, int[] coins) {
        //建表
        int n = coins.length;
        int[] dp = new int[amount + 1];
        //初始化
        dp[0] = 1;
        //填表
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= amount; j++) {
                dp[j] = dp[j];
                if (j >= coins[i - 1]) {
                    dp[j] += dp[j - coins[i - 1]];
                }
            }
        }
        //返回结果
        return dp[amount];
    }
}

四. 力扣 279. 完全平方数

1. 题目解析

2. 算法原理

完全背包原理相信大家已经掌握, 这里只简单点一下细节方面

3. 代码

java 复制代码
class Solution {
    public int numSquares(int n) {
        // 建表
        int sn = (int)Math.sqrt(n);
        int[][] dp = new int[sn + 1][n + 1];
        // 初始化
        for (int j = 1; j <= n; j++) {
            dp[0][j] = 0x3f3f3f3f;
        }
        // 填表
        for (int i = 1; i <= sn; i++) {
            for (int j = 0; j <= n; j++) {
                dp[i][j] = dp[i - 1][j];
                if (j >= i * i) {
                    dp[i][j] = Math.min(dp[i][j], dp[i][j - i * i] + 1);
                }
            }
        }
        // 返回结果
        return dp[sn][n];
    }
}

4. 优化后的代码

删除 i 维度, j 的范围缩小, 优化的算法原理推导过程在第一题

java 复制代码
class Solution {
    public int numSquares(int n) {
        // 建表
        int sn = (int) Math.sqrt(n);
        int[] dp = new int[n + 1];
        // 初始化
        for (int j = 1; j <= n; j++) {
            dp[j] = 0x3f3f3f3f;
        }
        // 填表
        for (int i = 1; i <= sn; i++) {
            for (int j = i * i; j <= n; j++) {
                dp[j] = Math.min(dp[j], dp[j - i * i] + 1);
            }
        }
        // 返回结果
        return dp[n];
    }
}
相关推荐
CCblandy4 小时前
向量检索 ANN 参数实测:换一种数据分布,recall 掉了 51 个百分点
人工智能·python·算法·自然语言处理·性能优化
CS创新实验室4 小时前
一棵树怎么做到“自平衡还不太费力“?——红黑树的旋转与染色
数据结构·算法·红黑树·408·计算机考研
Logic1014 小时前
C语言/数据结构位运算题解:异或XOR找出被挑选一次的果子编号——只出现一次的数字
c语言·数据结构·算法·数组·位运算·时间复杂度·空间复杂度
深圳市快瞳科技有限公司4 小时前
面向端侧部署的多帧宠物身份识别:完整性评分机制与工程约束
人工智能·算法·计算机视觉·端侧ai·宠物识别·宠物ai·宠物身份识别
云和数据.ChenGuang4 小时前
print(f“a+b=“,result) print(f“a+b={result}“)区别
java·前端·人工智能·python·深度学习
2601_962885724 小时前
如何用 Python 算最大回撤和回撤恢复期?
java·数据库·python
毕业设计7035 小时前
(免费领源码)SSM董家湾线上点餐系统04682- java、PHP、python、C#、小程序、大数据、单片机、网络工程等)
java·redis·mysql·tomcat·maven·mybatis·ssm
Q26433650235 小时前
【有源码】基于SpringBoot+Vue的居民健康体检与慢病干预管理系统-面向智慧医疗的体检预约与健康知识服务平台
java·vue.js·spring boot·mysql·毕业设计·javaweb·课程设计
悠仁さん6 小时前
非类型模板参数的特化与偏特化
java·数据结构·算法