当前位置: 首页 > ai >正文

完全背包(模板)

前言:完全背包问题,只是在0,1背包问题上稍微改变了一点,就是这里的物品可以重复选择。

例题1:完全背包__牛客网         

1. 题目解析(这个题目说的很清楚了)

(1)求这个背包至多能装多大价值的物品?

(2)若背包恰好装满,求至多能装多大价值的物品?

2. 算法原理

1.状态表示

根据0,1背包问题,

我们对于 问题一  设:f[ i ][ j ]表示在前 i 个物品中选出的物品 总体积不大于j的总质量最大值;

问题二  设:g[ i ] [ j ] 表示在前 i 个物品中选出的物品 总体积等于j的总质量最大值;

只是我们这里需要,对 i 位置的物品 进行枚举,i 位置的物品可能是 1个,2个,3个,......

(满足j >= n * v[ i ]  就行 )

2.状态表示转移方程

对于i位置的物品 : 选   dp[ i ][ j ]  =             满足 (j  >=   n * v [ i ])  即可

Math.max( dp[ i - 1 ][ j ] ,dp[ i - 1 ] [ j - v[ i ]] + w[ i ],dp[ i - 1][ j - 2 * v[ j ]] + 2 * w[ i ] + ....)  

不选: dp[ i ][ j ] = dp[i - 1][ j ]  

3.初始化

int[ ][ ] f = new int[n + 1][v + 1];   int[ ][ ] g = new int[n + 1][v + 1];

对于这多加的一行,一列,初始化的值应为0。(原因如下:)

对于 0 个物品,背包体积无论多大,能选出物品价值都为0;

对于无数个物品,背包体积为 0 ,能选出物品价值都为0;

4.填表顺序

对于dp[ i ][ j ] 依赖与 dp[ i - 1][ j ] , dp[ i - 1][ j - xx] 也就是左上方的值;所以我们

从 左 -> 右,上 -> 下 填表。 

5.返回值

由状态表示可得: 问题1为 f[ n ][ v ]  ; 问题2为 f[ n ][ v ]。 

3. 代码

public class Solution {public ArrayList<Integer> knapsack (int v, int n,ArrayList<ArrayList<Integer>> nums) {int[][] f = new int[n + 1][v + 1];int[][] g = new int[n + 1][v + 1];for (int i = 1; i <= n; i++) {for (int j = 1; j <= v; j++) {int tep = nums.get(i - 1).get(0);for (int k = 0; k * tep <= j; k++) {f[i][j] = Math.max(f[i][j], f[i - 1][j]);g[i][j] = Math.max(g[i][j], g[i - 1][j]);if (tep <= j) {f[i][j] = Math.max(f[i][j], f[i - 1][j - tep * k] + nums.get(i - 1).get(1) * k);}if(g[i-1][j - k*tep] != 0 || k * tep == j){g[i][j] = Math.max(g[i][j],g[i-1][j- k*tep] + nums.get(i - 1).get(1) * k);}}}}ArrayList<Integer> ret = new ArrayList<>();ret.add(f[n][v]);ret.add(g[n][v]);return ret;}
}

4. 优化:(实现 O(n3)  ->  O(n2) )(这一步说实话真的太强了)

等式一:

f[i][j] = max(f[i-1][j],  f[i-1][j - v[i]]+w[i], f[i-1][j-2*v[i]]+2*w[i], f[i-1][j-3*v[i]]+3*w[i] ,......)

使用代入法,将j= j - v[i]带入上面的方程中得到:

等式二:

f[i][j-v[i]] = max(f[i-1][j-v[i]],  f[i-1][j - 2*v[i]]+w[i], f[i-1][j-3*v[i]]+2*w[i], f[i-1][j-3*v[i]]+3*w[i] ,......)

对比等式一,等式二,可化简为:  (如此便可代替循环选多少个当前物品最佳)

f[i][j] = max(f[i-1][j],  f[i][j - v[i]]+w[i]);
import java.util.*;public class Solution {public ArrayList<Integer> knapsack (int v, int n, ArrayList<ArrayList<Integer>> nums) {int[][] f = new int[n + 1][v + 1];int[][] g = new int[n + 1][v + 1];for (int i = 1; i <= n; i++) {for (int j = 1; j <= v; j++) {int tep = nums.get(i - 1).get(0);f[i][j] = f[i - 1][j];g[i][j] = g[i - 1][j];if (tep <= j) {f[i][j] = Math.max(f[i][j], f[i][j - tep] + nums.get(i - 1).get(1));}                    if(tep == j || (tep < j && g[i][j - tep] != 0) ){g[i][j] = Math.max(g[i][j],g[i][j- tep] + nums.get(i - 1).get(1));}}}ArrayList<Integer> ret = new ArrayList<>();ret.add(f[n][v]);ret.add(g[n][v]);return ret;}
}

例题2:最少的完全平方数_牛客题霸_牛客网  (变式)

1. 题目解析

通过举例表明题目意思

给出 n  = 9 , 可提供选择的数有 1 ,4,9,16,25,36...,81,每个数可重复选;

选出m 个数要求和为n,求最小值m,这里选9,m = 1;

2. 算法原理

1.状态表示

设:dp[ i ] [ j ]表示在前i个数中选出 x 个可重复的数 和等于n,x的最小值。

2.状态表示转移方程

由完全背包化简的表达过程得:

选 i 位置 dp[ i ] [ j ] = Math.min( dp [ i - 1][ j ], dp [ i ] [ j - i * i ] + 1),需满足条件

(i * i == j || (i * i < j && dp[i][j - i*i] != 0)) ;

不选:dp[ i ][ j ] = dp[ i - 1][ j ];

3.初始化

int[][] dp = new int[m+1][n+1];

对于dp[ 0 ][ xx ] 0 个可选的数,但要求选出数的和 和为 n ,这是不可能的,所以我们初始化为 0x3f3f3f3f,这样就选不到,这一行的值了。

对于 dp[ xx ][ 0 ] xx 给可选的数,但要求选出数的和为0,不选就可以了,所以初始化为0。

4.填表顺序

根据当前位置依赖之前位置的关系,这里是 左 -> 右,上 ->下。

5.返回值

根据状态表示的返回值为: dp [m]  [n]

3. 代码

import java.util.Scanner;public class Main {public static void main(String[] args) {// 处理初始化,输出Scanner sc = new Scanner(System.in);int n = sc.nextInt();int m = (int)Math.sqrt(n);int[][] dp = new int[m+1][n+1];for(int j = 0;j <= n;j++){dp[0][j] = 0x3f3f3f3f;}dp[0][0] = 0;// 核心代码for(int i = 1;i <= m;i++){for(int j = 1;j <= n;j++){dp[i][j] = dp[i-1][j];if(i * i == j || (i * i < j && dp[i][j - i*i] != 0)){dp[i][j] = Math.min(dp[i][j],dp[i][j - i*i] + 1);}}}System.out.println(dp[m][n]);}
}

上面如有表述不好的,欢迎评论区留言。                    

http://www.xdnf.cn/news/18316.html

相关文章:

  • webrtc中win端音频---windows Core Audio
  • 2025图表制作完全指南:设计规范、工具选型与行业案例
  • Chrome/360 浏览器扩展深度解析:内置扩展与普通扩展的实现机制对比
  • (栈)Leetcode155最小栈+739每日温度
  • 力扣 30 天 JavaScript 挑战 第37天 第九题笔记 知识点: 剩余参数,拓展运算符
  • Spring Boot集成腾讯云人脸识别实现智能小区门禁系统
  • 【C++去除整数某一位数字求新数和倍数保留2位小数控制】2022-10-22
  • 人工智能 -- 循环神经网络day1 -- 自然语言基础、NLP基础概率、NLP基本流程、NLP特征工程、NLP特征输入
  • 打造医疗新质生产力
  • 如何用算力魔方4060安装PaddleOCR MCP 服务器
  • visual studio更改git提交的用户名和邮件
  • Seaborn数据可视化实战:Seaborn基础与实践-数据可视化的艺术
  • 高效处理NetCDF文件经纬度转换:一个纯CDO驱动的Bash脚本详解
  • [大模型微调]基于llama_factory用 LoRA 高效微调 Qwen3 医疗大模型:从原理到实现
  • WPF中UI线程频繁操作造成卡顿的处理
  • 中文房间悖论:人工智能理解力的哲学拷问
  • 深度解析游戏引擎中的相机:视图矩阵
  • 小体积晶振1610/2016/3225选型参数
  • 小游戏AssetBundle加密方案解析
  • 5.Shell脚本修炼手册---Linux正则表达式(Shell三剑客准备启动阶段)
  • 电能质量监测装置 分布式光伏安全并网“准入证”
  • 8.21 随机森林
  • conda create 报错:Unable to read repodata JSON(镜像 pkgs/free 导致)
  • Neovim clangd LSP 配置出现 “attempt to call field ‘ge‘”
  • C# 13 与 .NET 9 跨平台开发实战(第一章:开发环境搭建与.NET概述-下篇)
  • 鸿蒙中基础耗时分析:Time分析
  • 音视频面试题集锦第 29 期
  • JetBrains Mono字体
  • Vue3组件系统完全指南:从入门到面试通关
  • (第二十期下)超链接的更多分类