蓝桥杯AcWing学习笔记 3-2简单DP的学习(附相关蓝桥真题:地宫取宝、波动数列)(Java)

有参加蓝桥杯的同学可以给博主点个关注,博主也在准备蓝桥杯,可以跟着博主的博客一起刷题。

蓝桥杯

我的AcWing

题目及图片来自蓝桥杯C++ AB组辅导课

简单DP

先讲一些基础的DP问题,后续会更新复杂DP。

非传统DP问题思考方式,全新的DP思考方式:从集合角度来分析DP问题—— 闫式DP分析法

例题

AcWing 2. 01背包问题

模板题

01背包问题:有 N N N个物品,和一个容量是 V V V的背包,每一个物品有两个属性:体积 V i V_{i} Vi ,价值 W i W_{i} Wi ,每件物品只能用一次,要么用0次,要么用1次,所以称为01背包,问题为在背包装得下的情况下我们能挑出来的最大物品价值是多少。

利用闫式DP法分析:

image-20220214194903094

状态表示 f(i, j): 只从前 i i i 个物品中选并且总体积小于等于 j j j 的选法的集合,存的数是集合里面每一个选法的总价值的最大值

状态计算 f(i ,j): f ( i , j ) = M a x ( f ( i − 1 , j ) , f ( i − 1 , j − f(i, j) = Max(f(i - 1, j), f(i - 1,j - f(i,j)=Max(f(i1,j),f(i1,j V i V_{i} Vi) + W i + W_{i} +Wi ) ) )

二维(基础写法)

import java.util.Scanner;

public class Main {
    
    static final int N = 1010;
    static int[] v = new int[N];
    static int[] w = new int[N];
    static int[][] f = new int[N][N];
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        for (int i = 1; i <= n; i++) {
            v[i] = sc.nextInt();
            w[i] = sc.nextInt();
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= m; j++) {
                f[i][j] = f[i - 1][j];
                // 需要判断一下 j有可能装不下第i个物品
                if (j >= v[i]) f[i][j] = Math.max(f[i][j], f[i - 1][j - v[i]] + w[i]);
            }
        }
        System.out.print(f[n][m]);
    }
}

一维优化

f(i)只用到了f(i - 1)这一层,f(j)一定小于等于j,我们可以用一维数组来优化代码

import java.util.Scanner;

public class Main {
    
    static final int N = 1010;
    static int[] v = new int[N];
    static int[] w = new int[N];
    static int[] f = new int[N];
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        for (int i = 1; i <= n; i++) {
            v[i] = sc.nextInt();
            w[i] = sc.nextInt();
        }
        for (int i = 1; i <= n; i++) {
            /*for (int j = 0; j <= m; j++) {
                f[i][j] = f[i - 1][j] → f[j] = f[j];变为一维数组本行代码变为恒等式 即可直接删掉
                if (j >= v[i]) 判断可以去掉 设j循环开始的值为v[i] for (int j = v[i]; j <= m; j++)
                f[i][j] = Math.max(f[i][j], f[i - 1][j - v[i]] + w[i]) → f[j] = Math.max(f[j], f[j - v[i]] + w[i]);
                上面这行直接变为一维数组是不行的 j - v[i]严格小于j 我们的j是从小到大枚举的
                我们的j - v[i]在第i层已经被计算过了 f[j - v[i]]其实是第i层的j - v[i]
                原式:f[i - 1][j - v[i]] + w[i] 直接变成一维:f[j - v[i]] + w[i] 是不对的
                如何解决这个问题?把j循环变成从大到小枚举 for (int j = m; j >= v[i]; j--) 让j - v[i]晚一步更新
            }*/
            for (int j = m; j >= v[i]; j--) {
                f[j] = Math.max(f[j], f[j - v[i]] + w[i]);
            }
        }
        System.out.print(f[m]);
    }
}

AcWing 1015. 摘花生

只能向下和向右走 求摘到最大花生数

看样例:

输入样例:

2 → 几组数据
2 2 → 第一组数据的行列
1 1
3 4
2 3 → 第二组数据的行列
2 3 4
1 6 5

image-20220214222201821

所以输出8和16。

利用闫式DP法分析:

image-20220214235628055

import java.util.Scanner;

public class Main {
    
    static final int N = 110;
    static int[][] w = new int[N][N];
    static int[][] f = new int[N][N];

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int T = sc.nextInt();
        while (T-- != 0) {
            int n = sc.nextInt();
            int m = sc.nextInt();
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= m; j++) {
                    w[i][j] = sc.nextInt();
                    f[i][j] = Math.max(f[i - 1][j] + w[i][j], f[i][j - 1] + w[i][j]);
                }
            }
            System.out.println(f[n][m]);
        }
    }
}

AcWing 895. 最长上升子序列

模板题

image-20220215183153697

import java.util.Scanner;

public class Main {
    
    static final int N = 1010;
    static int a[] = new int[N];
    static int f[] = new int[N];

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        for (int i = 1; i <= n; i++) a[i] = sc.nextInt();
        
        for (int i = 1; i <= n; i++) {
            f[i] = 1; // 倒数第二个数不存在 序列中只有1个数
            for (int j = 1; j < i; j++) {
                if (a[j] < a[i]) f[i] = Math.max(f[i], f[j] + 1);
            }
        }
        int res = 0;
        for (int i = 1; i <= n; i++) res = Math.max(res, f[i]); // 遍历f数组取max
        System.out.println(res);
    }
}

第五届2014年蓝桥杯真题

AcWing 1212. 地宫取宝

JavaB组第9题

是上面两个题:摘花生和最长上升子序列模型的结合版,只能向下或者向右走,拿着的宝贝价值要递增。

初步枚举我们要枚举一个四维的状态,f(i, j, k, c),i 和 j 是坐标,k是个数,c是价值;然后还要计算我们的状态转移方程,总共要五重for循环。

利用闫式DP法分析:

image-20220216135703670

需要初始化:① f(1, 1, 1, w(1, 1)) = 1(取第一个数)② f(1, 1, 0, -1) = 1(不取第一个数),f表示的是方案数,不取也是一种方案,宝物的价值是可以为0的,所以当手中最大宝物的价值为-1时才能保证可以随便取任意价值的宝物,因为数组下标没法存负值,所以我们将所有宝物的价值都+1即可。

import java.util.Scanner;

public class Main {
    
    static final int N = 55, MOD = 1000000007;
    static int[][] w = new int[N][N];
    static int[][][][] f = new int[N][N][13][14];
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int k = sc.nextInt();
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                w[i][j] = sc.nextInt();
                w[i][j]++; // 数组下标没法存负值 所以每个宝物值都+1
            }
        }
        
        // 初始化
        f[1][1][1][w[1][1]] = 1; // 取第一个物品 是1种方案
        f[1][1][0][0] = 1; // 不取第一个物品 也是1种方案
        
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j<= m; j++) {
                if (i == 1 && j == 1) continue; // 因为前面已经初始化 所以直接continue 这行加上可以让代码更加圆滑
                for (int u = 0; u <= k; u++) { // 遍历物品个数 因为可以是0个物品 故从0开始遍历
                    for (int v = 0; v <= 13; v++) {
                        int val = f[i][j][u][v]; // 用val存值方便后续计算
                        val = (val + f[i - 1][j][u][v]) % MOD; // 两个数相加就要取模 三个数相加会爆int
                        val = (val + f[i][j - 1][u][v]) % MOD;
                        if (u > 0 && w[i][j] == v) { // 一定要保证最少有1个物品 物品的价值为c
                            for (int c = 0; c < v; c++) { // c'的遍历
                                val = (val + f[i - 1][j][u - 1][c]) % MOD;
                                val = (val + f[i][j - 1][u - 1][c]) % MOD;
                            }
                        } 
                        f[i][j][u][v] = val; // 没有像C++那样的&引用 所以要把值赋回去
                    }
                }
            }
        }
        
        int res = 0;
        for (int c = 0; c <= 13; c++) res = (res + f[n][m][k][c]) % MOD;
        System.out.print(res);
    }
}

AcWing 1214. 波动数列

JavaA组第10题

背包问题(组合模型)”简单“ 的变种😭,不愧是A组压轴题

经过推导我们得出结论,求: ( n − 1 ) (n - 1) (n1) d 1 + ( n − 2 ) d_{1} + (n - 2) d1+(n2) d 2 d_{2} d2 + . . . + + ... + +...+ d n − 1 d_{n-1} dn1 S S S n n n 的余数相同

image-20220216194942537

最终得出答案: f ( i − 1 , f(i - 1, f(i1, ( j − i ⋅ a ) (j - i·a) (jia) % n ) n) n)

image-20220216195844485

综上,利用闫式DP分析法分析:

image-20220216203658084

初始化:f(0, 0) = 1,一项都不选,总和是0,模n余数是0,方案数为1。

import java.util.Scanner;

public class Main {
    
    static final int N = 1010, MOD = 100000007;
    static int[][] f = new int[N][N];
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int s = sc.nextInt();
        int a = sc.nextInt();
        int b = sc.nextInt();
        
        f[0][0] = 1;
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < n; j++) {
                f[i][j] = (f[i - 1][get_mod(j - a * i,  n)] + f[i - 1][get_mod(j + b * i, n)]) % MOD;
            }
        }
        System.out.print(f[n - 1][get_mod(s, n)]);
    }
    
    private static int get_mod(int a, int b) { // 求a除以b的正余数
        return (a % b + b) % b;
    }
}

有对代码不理解的地方可以在下方评论

  • 47
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 38
    评论
鉴于提供的内容是一段包含三个不同的Java程序的代码,我无法提供一个完整的答案来回答你的问题。但是,我可以为你提供一些信息。 根据引用和引用的内容,这两个程序都涉及到动态规划算法。引用中的程序解决了一个关于序列的最长递增子序列的问题,而引用中的程序解决了一个关于背包问题的二维动态规划的问题。 对于引用中的内容,它是一个程序解决的是一个最长上升子序列的问题。 根据提供的代码,这些程序可能是用来解决一些蓝桥杯竞赛的题目。然而,我无法提供更多关于具体问题的信息。 如果你需要帮助解决特定的问题,我建议你仔细阅读每个程序的代码并理解它们的功能,然后根据需要进行相应的修改。如果你有关于动态规划蓝桥杯竞赛的更具体的问题,我会很愿意帮助你。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [蓝桥杯AcWing学习笔记 3-2简单DP学习相关蓝桥真题地宫取宝波动数列)(Java)](https://blog.csdn.net/weixin_53407527/article/details/123122245)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小成同学_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值