蓝桥杯 Java B 组之背包问题、最长递增子序列(LIS)

news/2025/2/22 15:58:55

Day 4:背包问题、最长递增子序列(LIS)


📖 一、动态规划(Dynamic Programming)简介

动态规划是一种通过将复杂问题分解成更小的子问题来解决问题的算法设计思想。它主要用于解决具有最优子结构重叠子问题的问题。

  • 最优子结构:问题的最优解可以通过子问题的最优解组合得到。
  • 重叠子问题:子问题的解可以在多次计算中复用,避免了重复计算。

在使用动态规划时,通常会构造一个“状态转移方程”,该方程描述了如何通过子问题的解来得到当前问题的解。


📖 二、背包问题

01背包问题(0/1 Knapsack)

问题描述: 给定一个背包和若干个物品,每个物品有一个重量和价值。求背包能够承载的最大价值。

  • 背包容量:W
  • 物品数目:n
  • 每个物品的重量和价值分别为 w[i]v[i]
  • 我们需要选择一些物品放入背包,使得背包的总价值最大。

思路与分析

  1. 状态定义
    • 定义 dp[i][w] 为前 i 个物品中,放入背包容量为 w 时能够达到的最大价值。
  2. 状态转移
    • 如果不选择第 i 个物品:dp[i][w] = dp[i-1][w]
    • 如果选择第 i 个物品:dp[i][w] = dp[i-1][w - weight[i]] + value[i],前提是 w >= weight[i]
  3. 最终的答案为 dp[n][W]

代码实现(01背包问题)

public class Knapsack {
    public int knapsack(int W, int[] weights, int[] values, int n) {
        // dp[i][w]表示前i个物品,背包容量为w时的最大价值
        int[][] dp = new int[n + 1][W + 1];
        
        // 填表,i表示物品数量,w表示背包容量
        for (int i = 1; i <= n; i++) {
            for (int w = 1; w <= W; w++) {
                // 不选择第i个物品
                dp[i][w] = dp[i - 1][w];
                
                // 选择第i个物品,前提是背包容量足够
                if (w >= weights[i - 1]) {
                    dp[i][w] = Math.max(dp[i][w], dp[i - 1][w - weights[i - 1]] + values[i - 1]);
                }
            }
        }
        
        return dp[n][W];
    }

    public static void main(String[] args) {
        Knapsack knapsack = new Knapsack();
        int[] weights = {2, 3, 4, 5}; // 物品的重量
        int[] values = {3, 4, 5, 6};  // 物品的价值
        int W = 5;  // 背包容量
        int n = weights.length; // 物品数量
        
        int maxValue = knapsack.knapsack(W, weights, values, n);
        System.out.println("最大价值: " + maxValue); // 输出 7
    }
}

代码讲解

  1. 二维DP数组dp[i][w] 表示前 i 个物品,背包容量为 w 时能达到的最大价值。
  2. 状态转移:通过选择或不选择第 i 个物品来更新 dp[i][w]
  3. 时间复杂度:O(n * W),其中 n 是物品的数量,W 是背包容量。

📖 三、最长递增子序列(LIS)

问题描述: 给定一个整数数组,求该数组的最长递增子序列(LIS)的长度。

  • 数组的递增子序列是数组中的一个子序列,并且这个子序列中的元素是严格递增的。
  • 我们要求的是最长递增子序列的长度。

思路与分析

  1. 状态定义
    • 定义 dp[i] 表示以 nums[i] 结尾的最长递增子序列的长度。
  2. 状态转移
    • 对于每个元素 nums[i],检查它之前的所有元素 nums[j],如果 nums[i] > nums[j],则更新 dp[i]dp[j] + 1
  3. 最终的答案是 dp 数组中的最大值。

代码实现(LIS)

public class LIS {
    public int lengthOfLIS(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }

        int n = nums.length;
        int[] dp = new int[n];
        // 初始化dp数组,每个位置的初始值都是1
        for (int i = 0; i < n; i++) {
            dp[i] = 1;
        }

        // 枚举每个元素,检查之前的元素
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (nums[i] > nums[j]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
        }

        // 找出dp数组的最大值,即最长递增子序列的长度
        int maxLength = 0;
        for (int length : dp) {
            maxLength = Math.max(maxLength, length);
        }
        
        return maxLength;
    }

    public static void main(String[] args) {
        LIS lis = new LIS();
        int[] nums = {10, 9, 2, 5, 3, 7, 101, 18};
        System.out.println("最长递增子序列的长度: " + lis.lengthOfLIS(nums)); // 输出 4
    }
}

代码讲解

  1. 动态规划数组dp[i] 表示以 nums[i] 为结尾的最长递增子序列的长度。
  2. 双重循环:对于每个元素 nums[i],检查它之前的元素 nums[j],如果满足递增条件,更新 dp[i]
  3. 最终结果:在 dp 数组中找出最大的值即为最长递增子序列的长度。

时间复杂度

  • O(n^2),因为每个元素都需要与之前的所有元素比较。

📖 四、总结

背包问题常考点

  1. 01背包问题:经典的动态规划问题,重点是理解状态转移方程,通过选择与不选择物品来更新背包容量的最大价值。
  2. 背包问题优化:可以使用滚动数组优化空间复杂度,将 dp 数组从二维优化为一维。

最长递增子序列(LIS)常考点

  1. 状态转移:LIS 是一个非常经典的动态规划问题。每个 dp[i] 由之前的所有 dp[j] 推导出。
  2. 优化空间复杂度:O(n^2) 的时间复杂度较高,可以通过二分查找等优化方法将时间复杂度降到 O(n log n)。

常见易错点

  1. 背包问题:忘记更新 dp[i][w],或者状态转移写反了,导致背包容量或物品数目错误。
  2. LIS问题:对比 nums[i] > nums[j] 时,处理不当可能导致未能正确记录递增关系。


http://www.niftyadmin.cn/n/5862492.html

相关文章

自动驾驶之BEV概述

1、为什么需要BEV&#xff1f; 自动驾驶需要目标在3D空间的位置信息&#xff0c;传统检测为2D图像上检测目标然后IPM投影到3D。所以无论如何3D结果才是我们最终想要的。 对于单个传感器&#xff1a;通过单目3D、深度估计等手段好像能解决这个问题&#xff0c;但是往往精度不高…

Spring Boot(十六):使用 Jenkins 部署 Spring Boot

Jenkins 是 Devops 神器&#xff0c;本篇文章介绍如何安装和使用 Jenkins 部署 Spring Boot 项目 Jenkins 搭建、部署分为四个步骤&#xff1b; 第一步&#xff0c;Jenkins 安装 第二步&#xff0c;插件安装和配置 第三步&#xff0c;Push SSH 第四步&#xff0c;部署项目 第…

ubuntu部署小笔记-采坑

ubuntu部署小笔记 搭建前端控制端后端前端nginx反向代理使用ubuntu部署nextjs项目问题一 如何访问端口号配置后台运行该进程pm2 问题二 包体过大生产环境下所需文件 问题三 部署在vercel时出现的问题需要魔法访问后端api时&#xff0c;必须使用https协议电脑端访问正常&#xf…

DuodooBMS源码解读之 odoo_phoenix_alarm模块

Odoo18 扩展模块声光报警器用户使用手册 一、模块概述 本扩展模块是基于 Odoo18 原生系统进行开发的&#xff0c;主要用于实现与上位声光报警设备的通讯功能。通过该模块&#xff0c;用户可以方便地向设备发送指令&#xff0c;控制设备的声音、灯光等操作。本手册将详细介绍该…

DeepSeek接入Siri(已升级支持苹果手表)完整版硅基流动DeepSeek-R1部署

DeepSeek接入Siri&#xff08;已升级支持苹果手表&#xff09;完整版硅基流动DeepSeek-R1部署 **DeepSeek** 是一款专注于深度学习和人工智能的工具或平台&#xff0c;通常与人工智能、机器学习、自动化分析等领域有关。它的主要功能可能包括&#xff1a;深度学习模型搜索&…

Python爬虫实战:获取12306特定日期、城市车票信息,并做数据分析以供出行参考

注意:以下内容仅供技术研究,请遵守目标网站的robots.txt规定,控制请求频率避免对目标服务器造成过大压力! 1. 核心思路 需求:获取明天(2025 年 2 月 21 日)从北京到上海的车次、票价、出发时间、硬卧二等卧信息,并保存到 CSV 文件,然后分析出价格最低的 10 趟车次。目…

Java四大框架深度剖析:MyBatis、Spring、SpringMVC与SpringBoot

目录 前言&#xff1a; 一、MyBatis框架 1. 概述 2. 核心特性 3. 应用场景 4. 示例代码 二、Spring框架 1. 概述 2. 核心模块 3. 应用场景 4. 示例代码 三、SpringMVC框架 1. 概述 2. 核心特性 3. 应用场景 4. 示例代码 四、SpringBoot框架 1. 概述 2. 核心…

基于COSTAR模型的内容创作:如何用框架提升写作质量

目录 前言1. Context&#xff08;上下文&#xff09;&#xff1a;理解背景&#xff0c;奠定写作基础1.1 何为上下文1.2 上下文的作用1.3 案例解析 2. Objective&#xff08;目标&#xff09;&#xff1a;明确写作方向&#xff0c;避免跑题2.1 确立目标2.2 如何设定目标2.3 案例…