动态规划算法(3)--0-1背包、石子合并、数字三角形

news2024/11/18 16:43:48

目录

一、0-1背包

1、概述

2、暴力枚举法

3、动态规划

二、石子合并问题

1、概述

2、动态规划

3、环形石子怎么办?

三、数字三角形问题

1、概述

2、递归

3、线性规划

 四、租用游艇问题 


一、0-1背包

1、概述

        0-1背包:给定多种物品和一个固定重量W的背包,每种物品有一个固定的重量w_i,价值v_i,现在要将物品装入背包,每种物品至多装入一个,使总重量不超过W,且总价值最大。

        约束条件和优化目标如下:

2、暴力枚举法

        暴力枚举法也是使用递归的方式,假设对前i个物品分析,总重量为c,当前总价值为P。那么存在递归条件:\begin{matrix} \quad \qquad p1=knapsack(i-1,c-w_i)\\ \quad p2=knapsack(i-1,c) \\ P=max(p1+v_i,p2) \end{matrix}

        另外当重量小于0时,总价值为-∞(代码中可以用-999替代),物品个数小于等于0,总价值为0。

        暴力枚举法依赖递归方式,没有减少子问题个数,所以根据递归树计算,复杂度为O(2^n)

        伪代码如下:

        代码实例: 

   //暴力枚举
   public static int violate_knapsack(int weight,int i,int weights[],int values[])
   {
        if (weight<0)
        {
            return -9999; 
        }
              
        if (i<=0)  
        {    
            return 0;
        }
        int p1=violate_knapsack(weight-weights[i],i-1,weights,values);   //选中i物品
        int p2=violate_knapsack(weight,i-1,weights,values);              //不选i物品
        int p=p1+values[i]>p2?p1+values[i]:p2;   
        return p;
   }

3、动态规划

        动态规划方法通过建立备忘录的方式,前i个物品,总重量为j的时刻永远依赖于前面的子结构。M数组为加入前i个物品后,总重量为j时的总价值,Rec数组表示是否有物品添加,若添加则为1,不添加则为0。

        原理:

参数:count代表总类别个数,weight代表背包重量,weights为物品重量,values为物品价值,name为物品名称。

(1)首先,M数组0行0列均为初值0,注意:行为重量,列为种类个数。

(2)按每行逐个值来填写M和Rec。如:前i个物品,总重量为j的情况下,即求M[i][j]时,如果该物品重量小于背包重量且在前i-1个物品,总重量为(j-当前物品重量)时的总重量+当前物品价值的总价值,大于前i-1个物品,总重量为j时的总价值时M填写较大的总价值,Rec=1。条件写为:weights[i-1]\leqslant j,values[i-1]+M[i-1][j-weights[i]]>M[i-1][j]

(3)由于调用j-当前物品重量时有可能为负数,所以保证最小值为0,设为minor。

(4)如果不满足(2)条件,则M[i][j]填上一行同列的M[i-1][j]值,即不选这个物品(索引为i-1)的总价值。Rec为0。

(5)最优解追寻:Rec数组倒序查找i取最大值,逐次减1,判断条件如果Rec数组为1,则从总重量weight中减少weights[i-1],输出name[i-1]物品,直到i取1。

//0-1背包问题
import java.util.ArrayList;
public class backage {
   public static void main(String []args)
   {
        int values[]={24,2,9,10,9};
        int weights[]={10,3,4,5,4};
        int count=5;int weight=13;
        int M[][]=new int [count+1][weight+1];
        int Rec[][]=new int [count+1][weight+1];
        String name[]={"beer","cocacola","cookie","bread","milk"};
        //暴力求解
        System.out.println(violate_knapsack(weight,count-1,weights,values));  
        //建立M数组,Rec数组 
        knapsack(count,weight,M,Rec,weights,values);
        //M数组
        for(int i=0;i<count+1;i++)
        {
            for(int j=0;j<weight+1;j++)
            {
                System.out.print(M[i][j]+"\t");
            }
            System.out.println("");
        }
        //Rec数组
        for(int i=0;i<count+1;i++)
        {
            for(int j=0;j<weight+1;j++)
            {
                System.out.print(Rec[i][j]+"\t");
            }
            System.out.println("");
        }
        //输出最优解
        Print(count,weight,Rec,name,weights);
   } 

   //动态规划
   public static void knapsack(int count,int weight,int M[][],int Rec[][],int weights[],int values[])
   {

        for(int i=0;i<count+1;i++)         //0行0列没有值参与
            M[i][0]=0;
        for(int j=0;j<weight+1;j++)
            M[0][j]=0;
        for(int i=1;i<count+1;i++)         //第一行的表示加入第一个物品,但是第一个物品在索引0处
        {
            for(int j=1;j<weight+1;j++)
            {
                int minor;                 //minor的设计是防止j-weights出现小于0,有可能背包重量小于上一行物品的重量,从而小于背包质量,小于背包质量默认为0
                if(j-weights[i-1]<0)
                    minor=0;
                else
                    minor=j-weights[i-1];
                if((weights[i-1]<=j) & (values[i-1]+M[i-1][minor]>M[i-1][j]))    //如果可以替换,M替换为新值,Rec更新为1
                {
                    M[i][j]=values[i-1]+M[i-1][minor];
                    Rec[i][j]=1;
                }
                else{
                    M[i][j]=M[i-1][j];                                           //不能修改时,M用上一行同列值替换,Rec更新为0
                    Rec[i][j]=0;
                }
            }
        }
   }

   //输出最优解
   public static void Print(int count,int weight,int Rec[][],String name[],int weights[])
   {
        for(int i=count;i>0;i--)
        {
            if(Rec[i][weight]==1)
            {
                System.out.println(name[i-1]);
                weight=weight-weights[i-1];
            }
        }
   }

M数组和Rec数组的写法:

二、石子合并问题

1、概述

        现在有n堆石子排成一排,要求将石子有次序地合并成一堆,规定每次只能选相邻的两堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分,设计一个算法将n堆石子合并成一堆的最小得分和最大得分。

        类比矩阵连乘问题求解,利用动态规划,设计m和s数组,最大值为m[1][n]。

2、动态规划

        动态规划转移方程(最小值):

        m[i][j]=\begin{Bmatrix} 0 \qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad \quad i=j\\ min(m[i][k]+m[k+1][j]+sum(i,j)) \quad i<j \end{Bmatrix}

        动态规划转移方程(最大值):

 m[i][j]=\begin{Bmatrix} 0 \qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad \quad i=j\\ max(m[i][k]+m[k+1][j]+sum(i,j)) \quad i<j \end{Bmatrix}

3、环形石子怎么办?

        如果题目要求石子堆围成一圈,其他要求不变,我们可以考虑将数组变为一个重复数组,由环形变为线性。如{1,2,3}则变为{1,2,3,1,2,3},n=2*n,然后考虑(j-i)>=arr_length时才满足原来的最多三个石子堆合并的问题。

        动态规划转移方程相同,只是多了一个退出循环的条件,同样的生成m数组,假设石子堆为{4,4,5,9},当前生成最小值m数组应该为下图示例:

        可以看到原先的m[1][n]为最小值,现在应该讨论min(m[i][n+i-1]) \quad 1\leqslant i\leqslant n,也就是四个数的最小值。

        代码示例:

//石子合并问题
public class stonemerge {
   public static void main(String[] args)
   {
        
        int arr[]={4,4,5,9};
        int n=arr.length;
        int m[][]=new int[n+1][n+1]; int m_[][]=new int[n*2+1][n*2+1];
        int s[][]=new int[n+1][n+1]; 
        minserge(arr,m_);
        System.out.println(trackmin(arr, m_));
        maxserge(arr, m_);
        System.out.println(trackmax(arr,m_));
   }
   //最小合并m数组 
   public static void minserge(int arr_[],int m[][])
   {
        int n=arr_.length*2;
        int arr[]=new int[n];
        add_arr(arr,arr_);
        for(int i=1;i<n;i++)
            m[i][i]=0;
        for (int r = 2; r <=n; r++) 
        {
			for (int i = 1; i <= n - r + 1; i++) 
            {               
				int j = i + r - 1;
                if((j-i)>=arr_.length)
                    break;                
                m[i][j] = m[i + 1][j] + sum(i,j,arr);				             
				for (int k = i + 1; k < j; k++)
                 {
					int t = m[i][k] + m[k + 1][j] + sum(i,j,arr);
					if (t < m[i][j]) 
						m[i][j] = t;
				}
			}
		}
   }
   //最大合并m数组
   public static void maxserge(int arr_[],int m[][])
   {
        int n=arr_.length*2;
        int arr[]=new int[n];
        add_arr(arr,arr_);
        for(int i=1;i<n;i++)
            m[i][i]=0;
        for (int r = 2; r <=n; r++) 
        {
			for (int i = 1; i <= n - r + 1; i++) 
            {               
				int j = i + r - 1;
                if((j-i)>=arr_.length)
                    break;                
                m[i][j] = m[i + 1][j] + sum(i,j,arr);				             
				for (int k = i + 1; k < j; k++)
                 {
					int t = m[i][k] + m[k + 1][j] + sum(i,j,arr);
					if (t > m[i][j]) 
						m[i][j] = t;
				}
			}
		}
   }
   //合并代价
   public static int sum(int i,int j, int arr[])
   {
        int tot=0;
        for(int m=i-1;m<=j-1;m++)
            tot+=arr[m];
        return tot;
   }
   //环形转线性数组生成
   public static void add_arr(int arr[],int arr_[])
   {
        for(int i=0;i<arr.length;i++)
        {
            if(i<arr_.length)
                arr[i]=arr_[i];
            else
                arr[i]=arr_[i-arr_.length];
        }
   }
   //最小值
   public static int trackmin(int arr[],int m[][])
   {
        int n=arr.length;
        int min=m[1][n];
        for(int i=2;i<=n;i++)
        {
            if(m[i][n+i-1]<min)
                min=m[i][n+i-1];
        }
        return min;
   }
   //最大值
   public static int trackmax(int arr[],int m[][])
   {
        int n=arr.length;
        int max=m[1][n];
        for(int i=2;i<=n;i++)
        {
            if(m[i][n+i-1]>max)
                max=m[i][n+i-1];
        }
        return max;
   }
}

三、数字三角形问题

1、概述

        给定一个由n行数字组成的数字三角形,设计算法,计算从三角形的顶至底的一条路径,每次必须下降一层,使该路径经过数字总和最大,如下图路线7-3-8-7-5,总和30。

2、递归

递归条件:

nums[x][y]+=max(trest(x+1,y),test(x+1,y+1)) \ x\neq n-1

               当x==n-1时为最底层数字,返回该数字。

//递归方法
public static int test(int x,int y,int nums[][]) {
        int n=nums.length;
        if(x == n-1) {
            return nums[x][y];
        }
        return (nums[x][y] + (test(x+1,y,nums) >= test(x+1,y+1,nums) ? test(x+1,y,nums) : test(x+1,y+1,nums)));
    }

3、线性规划

        状态转移方程与递归条件一样,只不过从倒数第二层向上叠加(参数i),每层的值改变为当前层到底层的最大值,变量k遍历某一层的每个数字,计算到底层的最大值并保存。

//动态规划
public static int max(int nums[][])
{
        for(int i=nums.length-2;i>=0;i--)
        {
            int j=nums[i].length;
            for(int k=0;k<j;k++)
            {
                nums[i][k]+=(nums[i+1][k]>=nums[i+1][k+1]?nums[i+1][k]:nums[i+1][k+1]);
            }
        }
        return nums[0][0];
}

 四、租用游艇问题

        游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇,游艇出租站i到游艇出租站j之间的租金为r(i,j)(1<=i<j<=n),设计算法,计算出游艇出租站1到游艇出租站n的最少租金。

        输入的数字,第一行代表1到2的距离和1到3的距离,第二行代表2到3的距离。

//租用游艇问题
public class rentyacht {
   public static void main(String [] args)
   {
        int cost[][]={
            {5,15},
            {7}
        };
        int n=cost.length;
        System.out.println(rentmin(cost,n));
   } 
   public static int rentmin(int[][] cost, int n) {
        int[][] best = new int[n + 1][n + 1];
        for(int i = n - 1; i >= 1; i--) {
            best[i][n] = cost[i-1][n-1];
            for(int j = n - 1; j > i; j--) {
                best[i][n] = cost[i-1][j-1] + best[j][n]<best[i][n]?cost[i-1][j-1] + best[j][n]:best[i][n];
            }
        }
        return best[1][n];
    }
}

 

 

         

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1095490.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenCV完美实现两张图片的全景拼接(详细教程)

目录 1&#xff0c;主要步骤 1.1 导入需要的包和模块&#xff0c;并读取两张待拼接的图片&#xff0c;这里我们假设它们为 left.jpg 和 right.jpg。 1.2 创建SIFT检测器 1.3 创建一个基于 FLANN 的匹配器 1.4 筛选过程删除掉一些不合适的匹配点&#xff0c;只保留最好的…

[Spring] SpringMVC 简介(三)

目录 九、SpringMVC 中的 AJAX 请求 1、简单示例 2、RequestBody&#xff08;重点关注“赋值形式”&#xff09; 3、ResponseBody&#xff08;经常用&#xff09; 4、为什么不用手动接收 JSON 字符串、转换 JSON 字符串 5、RestController 十、文件上传与下载 1、Respo…

03_51单片机点亮LED灯

51单片机是一种非常常见的单片机型号&#xff0c;广泛应用于各种嵌入式系统和电子设备中。LED灯是一种常见的输出设备&#xff0c;用于显示信息或指示状态。下面是关于51单片机控制LED灯的介绍&#xff1a; 1. 连接LED灯&#xff1a;将LED的正极连接到51单片机的一个I/O引脚&a…

英语——歌曲篇——All Out Of Love

All Out Of Love [Air Supply失落的爱] 歌词 I’m lying alone with my head on the phone Thinking of you till it hurts I know you hurt too but what else can we do Tormented and torn apart I wish I could carry your smile in my heart For times when my life se…

MacOS ventura跳过配置锁

Macbook pro 2021跳配置锁 1.什么是配置锁&#xff1f; 配置锁顾名思义就是美国一些企业和公司向苹果工公司定制采购的机器&#xff0c;这些机器一般供应内部员工使用&#xff0c;这种机器和正常机没有什么区别&#xff0c;也是无锁三网机器&#xff0c;功能和正常机器一摸一…

去雨去雪去雾算法本地实验速度对比

在进行去雨去雪去雾算法的实验过程中&#xff0c;博主使用自己的电脑进行实验&#xff0c;尽管能够成功运行&#xff0c;但速度却属实不尽人意&#xff0c;博主的笔记本显卡是GTX960&#xff0c;显存为4G&#xff0c;在进行实验的过程中&#xff0c;batch-size只能设置为3&…

找不到mfc140u.dll,无法继续执行代码 cdr

在计算机系统中&#xff0c;DLL文件&#xff08;动态链接库&#xff09;是一种非常重要的资源。它们包含了许多可以在程序运行时被调用的代码和数据。然而&#xff0c;当某个特定的DLL文件丢失或损坏时&#xff0c;可能会导致程序无法正常运行。本文将详细介绍五个解决mfc140u.…

flink教程

文章目录 来自于尚硅谷教程1. Flink概述1.1 特点1.2 与SparkStreaming对比 2. Flink部署2.1 集群角色2.2 部署模式2.3 Standalone运行模式2.3.1 本地会话模式部署2.3.2 应用模式 2.4 YARN运行模式2.4.1 会话模式部署2.4.2 应用模式部署 2.5 历史服务 3. 系统架构3.1 并行度3.2 …

线性回归原理

1、 线性回归的原理 1.1 线性回归应用场景 房价预测 销售额度预测 金融:贷款额度预测、利用线性回归以及系数分析因子1.2 什么是线性回归 1.2.1定义与公式 线性回归(Linear regression)是利用回归方程(函数)对一个或多个自变量(特征值)和因变量(目标值)之间关系进行建模的…

在Unity中挂载C#脚本的三种方法

第一种 ①在Project&#xff08;工程&#xff09;窗口的某个文件夹中&#xff08;也可以选择新建在Assets&#xff08;资源根目录&#xff09;中&#xff09;&#xff0c;然后单击鼠标右键&#xff0c;选择Create->C# Script 注意&#xff1a;扩展名在Unity编辑器中是隐藏…

测试中Android与IOS分别关注的点

目录 1、自身不同点 2、测试注重点 3、其他测试点 主要从本身系统的不同点、系统造成的不同点、和注意的测试点做总结 1、自身不同点 研发商&#xff1a;Adroid是google公司做的手机系统&#xff0c;IOS是苹果公司做的手机系统   开源程度&#xff1a;Android是开源的&a…

微信发红包(各种红包类型)-测试用例设计

微信发红包&#xff08;各种红包类型&#xff09;

基于Java的宠物商城管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

Docker逃逸---procfs文件挂载

一、产生原因 将宿主机/proc目录挂载进了容器&#xff0c;而该目录内的/proc/sys/kernel/core_pattern文件是负责进程奔溃时内存数据转储的&#xff0c;当第一个字符是| 管道符时&#xff0c;后面的部分会以命令行的方式进行解析并运行&#xff0c;攻击者可以将恶意文件写入该…

Redis常用数据类型、Redis常用命令

Redis常用数据类型、Redis常用命令&#xff1a; Redis常用数据类型&#xff1a;1. 字符串String 类型2. 哈希hash 类型3. 列表list 类型4. 集合set 类型5. 有序集合sorted set / zset 类型 Redis常用命令&#xff1a;1. 字符串操作命令2. 哈希操作命令3. 列表操作命令4. 集合操…

数据结构--》掌握数据结构中的排序算法

当我们面对海量数据时&#xff0c;如何高效地将其排序是数据结构领域中一个重要的问题。排序算法作为其中的关键部分&#xff0c;扮演着至关重要的角色。 无论你是初学者还是进阶者&#xff0c;本文将为你提供简单易懂、实用可行的知识点&#xff0c;帮助你更好地掌握排序算法在…

如何根据Mapper的Class反向生成SQL文件创建数据库表

微信公众号&#xff1a;大数据高性能计算 问题&#xff1a;已经根据数据库表生成了一堆的Mapper Class&#xff0c;但是当另一个新同学复制项目的时候没有SQL建表文件&#xff0c;导致无法创建数据库&#xff0c;那么这时候我们就需要通过反射以及注解相关的解析自动去生成数据…

Springboot-MyBatisPlue入门

一 创建项目&#xff0c;选择spring boot 初始化&#xff0c;配置相关信息 第五步创建实体类 二 快速开发实体类的jar包--lombok <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12<…

openGauss学习笔记-100 openGauss 数据库管理-管理数据库安全-客户端接入之用SSL进行安全的TCP/IP连接

文章目录 openGauss学习笔记-100 openGauss 数据库管理-管理数据库安全-客户端接入之用SSL进行安全的TCP/IP连接100.1 背景信息100.2 前提条件100.3 注意事项100.4 操作步骤100.5 相关参考 openGauss学习笔记-100 openGauss 数据库管理-管理数据库安全-客户端接入之用SSL进行安…

Vue-3.4Vuex

Vuex概述 是什么&#xff1a; vuex是一个vue的状态管理工具&#xff0c;状态就是数据。 Vuex是一个插件&#xff0c;可以帮我们管理vue通用的数据&#xff08;多组件共享的数据&#xff09; 例如&#xff1a;购物车数据、个人信息数据 场景&#xff1a; 1&#xff09;某个…