算法——动态规划(DP)——递推

这篇具有很好参考价值的文章主要介绍了算法——动态规划(DP)——递推。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

DP

  • 动态规划常用于解决优化问题。

  • 动态规划通常以自底向上或自顶向下的方式进行求解。

    • 自底向上的动态规划从最简单的子问题开始,逐步解决更复杂的问题,直到达到原始问题。

    • 自顶向下的动态规划则从原始问题出发,分解成子问题,并逐步求解这些子问题。

  • 动态规划算法通常通过填表或递推的方式来求解最优解,以减少重复计算,提高计算效率。

  • 动态规划可以应用于诸如最短路径、最大价值、最长子序列等问题的求解,特别适用于解决寻找最优解或最优路径的问题,例如最长公共子序列、背包问题等。

  • 求解DP问题的步骤:
    • 1、定义状态
    • 2、状态转移 
      • 确定状态转移方程
    • 3、定义初始状态
  • DP可以解决的问题需满足三个条件:
    • 1、问题有最优解
    • 2、有大量子问题重复(DP可以把求解的结果存起来,后续用到时直接查询)
    • 3、当前阶段的求解只与前面的阶段有关,与之后的阶段无关

递推 

  • 递归与递推的区别
    • 递归将问题分解成更小的子问题,并通过解决这些子问题来解决原始问题。

      • 在编程中,递归函数是指在函数内部调用自身的函数。递归通常与基本情况(递归的终止条件)结合使用,以确保递归不会无限进行下去而导致栈溢出。

    • 递推是指根据已知的初始条件以及前一项的值推导出下一项的值。

      • 递推通常用于描述数列或序列的情况,例如斐波那契数列就是一个经典的递推序列,每一项都是前两项之和。

    • 分治算法策略主要通过递归实现;动态规划算法主要通过递推实现

  • 递推法常被用于解决数论、组合数学、动态规划等方面的问题,比如求解斐波那契数列、阶乘、组合数等问题。使用递推法可以将复杂的问题简化,并通过寻找递推关系的规律,从而快速求解问题。

 二、求解斐波那契数列

public class Fibonacci {
    // 计算斐波那契数列的值,使用动态规划方法
    public int calculateFibonacci(int n) {
        if (n <= 1) {
            return n;  // 当n为0或1时,直接返回n,无需计算
        }
        // 创建一个数组来保存中间结果,避免重复计算
        int[] dp = new int[n + 1];
        dp[0] = 0; // 初始化数组第一个元素为0
        dp[1] = 1; // 初始化数组第二个元素为1
        // 从第三个位置开始计算斐波那契数列的值
        for (int i = 2; i <= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2];  // 根据动态规划定义计算第i个斐波那契数列的值
        }
        return dp[n];  // 返回第n个斐波那契数列的值
    }

    public static void main(String[] args) {
        Fibonacci fib = new Fibonacci();
        int n = 10;
        int result = fib.calculateFibonacci(n);
        System.out.println("第 " + n + " 个斐波那契数列的值为:" + result);
    }
}

 三、爬楼梯(一维)

假设有级楼梯,每次只能爬1级或2级,有多少种方法可以爬到楼梯的顶部?

分析:

  • 在爬上第 i 级楼梯之前 ,爬楼梯的人一定站在第 i-1 级楼梯或第 i-2 级楼梯上,两种情况
  • 所以爬上第 i 级楼梯的方法等于两种走法之和(站在第i-1级楼梯,站在第i-2级楼梯上)
  • 此处涉及到应用组合数学的加法规则:(“或”)
    • 如果一个事件以 a 种方式发生,第二个事件以 b 种(不同)方式发生,那么存在 a+b 种方式
  • dp[i]表示爬上第i级楼梯有多少种走法
    • dp[1]=1
    • dp[2]=2
    • dp[i]=dp[i-1]+dp[i-2],i>2(状态转移方程)

1、辅助数组

时间复杂度O(n),空间复杂度O(n)

package no1_1;
import java.util.Scanner;
public class example {
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		int[] dp=new int[n+1];
		dp[1]=1;
		dp[2]=2;
		for(int i=3;i<=n;i++) {
			dp[i]=dp[i-2]+dp[i-1];
		}
		System.out.println(dp[n]);
	}
}

2、只使用两个变量记录前两项的值

时间复杂度O(n),空间复杂度O(1)

package no1_1;
import java.util.Scanner;
public class example {
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		int val1=1,val2=2,result=0;;
		for(int i=3;i<=n;i++) {//val1:前一项;val2:当前项
			result=val1+val2;
			val1=val2;
			val2=result;
		}
		System.out.println(result);
	}
}

四、拿金币(二维)

有一个N x N的方格,每一个格子都有一些金币,只要站在格子里就能拿到里面的金币。你站在最左上角的格子里,每次可以从一个格子走到它右边或下边的格子里。请问如何走才能拿到最多的金币。

非线性dp,数据结构与算法(java版),算法,动态规划

分析:

  • 当前位的最大金币数,需要上一位也拿到最大金币数

  • 非线性dp,数据结构与算法(java版),算法,动态规划

package no1_1;
import java.util.Scanner;
public class Main {
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		int n = scanner.nextInt();
		//根据输入,把金币放入数组中对应的位置
		int[][] goldCoins = new int[n + 1][n + 1];
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				goldCoins[i][j] = scanner.nextInt();
			}
		}
		//该数组存储的是走到当前位置所拿的最多金币数
		int[][] sum = new int[n + 1][n + 1];
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				//当前位的最大金币数,需要上一位也拿到最大金币数,
				//该循环看的是sum[i][j],一直是往前走的,不要被i-1和j-1误了眼,觉得是倒退着走,i-1和j-1只是判断上一步是应该在哪里
				if (sum[i][j - 1] > sum[i - 1][j]) {
					sum[i][j] = sum[i][j - 1] + goldCoins[i][j];
				} else {
					sum[i][j] = sum[i - 1][j] + goldCoins[i][j];
				}
			}
		}
		
		System.out.println(sum[n][n]);
	}
}

五、印章(二维)

共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。

非线性dp,数据结构与算法(java版),算法,动态规划

分析:

  • i:买的印章数
  • j:凑齐的印章数
  • dp[i][j]:买了 i 个印章,凑齐了 j 种的概率
  • 概率 p=1 / n 
  • 情况一:
    • i < j
    • 不可能凑齐,dp[i][j]=0
  • 情况二:
    • j == 1
    • 买了 i 张印章,凑齐的印章为图案1时,概率为
    • 但有 n 种印章图案,总的概率等于每个种图案的概率和(应用组合数学的加法规则 )
    • 即,而 p = 1 / n,所以
  • 情况三:
    • i >= j

    • 为下面两种情况相加(应用组合数学的加法规则)

    • 1、买了 i - 1 张 已经得到了 j 种,最后一张随便

      • dp[i] [j] = dp[i-1] [j] * ( j / n )

        • dp[i-1] [j]是买了 i - 1 张 已经得到了 j 种的概率

        • j / n是最后一张随便哪种的概率

    • 2、买了 i - 1 张 只得到了 j - 1 种,最后一张是第 j 种

      • dp[i] [j] = dp[i-1] [j-1] * (n-(j-1)) / n

        • dp[i-1] [j-1]是买了 i - 1 张 只得到了 j - 1 种的概率

        • (n-(j-1)) / n是买最后一张是第 j 种的概率

package no1_1;
import java.util.*;
public class Main {
    public static void main(String[] args) {    	
        Scanner sc=new Scanner(System.in); 
        int n=sc.nextInt();
        int m=sc.nextInt();
        double[][] array=new double[m+1][n+1];
        System.out.printf("%.4f",probability(array,n,m));//动态规划
    }
    public static double probability(double[][] array,int n,int m) {
    	double p=1.0/n;
    	for(int i=1;i<=m;i++) {//买的印章数
    		for(int j=1;j<=n;j++) {//凑齐的印章数
    			if(i<j) {//买的印章数少于种类数,不可能凑齐
    				array[i][j]=0;
    			}else if(j==1) {//只凑齐了一种
    				array[i][j]=Math.pow(p, i-1);
    			}else {
    				//为下面两种情况相加,(应用组合数学的加法规则)
    				//1、买了 i - 1 张 已经得到了 j 种,最后一张随便, dp[i] [j] = dp[i-1] [j] * ( j / n )
    				//2、买了 i - 1 张 只得到了 j - 1 种,最后一张是第 j 种, dp[i] [j] = dp[i-1] [j-1] * (n-j+1) / n
    				array[i][j]=array[i-1][j]*(j*p)+array[i-1][j-1]*((n-j+1)*p);
    			}
    		}
    	}
    	return array[m][n];
    }
}

六、过河马

非线性dp,数据结构与算法(java版),算法,动态规划

非线性dp,数据结构与算法(java版),算法,动态规划

分析:

  •  马不会走回头路的意思是:跳的下一个点的行坐标必须大于现在的行坐标
  • 非线性dp,数据结构与算法(java版),算法,动态规划
import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner input=new Scanner(System.in);
        int n=input.nextInt();
        int m=input.nextInt();
        long[][] methods=new long[102][102];//methods[i] [j] 表示马从(1,1)跳到(i,j)的走法种数。
        int i,j;
        //初始状态
        methods[1][1]=0;
        methods[2][3]=1;
        methods[3][2]=1;

    	for(i=1;i<=n;i++){
    		for(j=1;j<=m;j++){
    			if(i-2>=1&&j-1>=1){
    				methods[i][j]+=methods[i-2][j-1];//马从(i-2,j-1)跳到(i,j),则点(i,j)的走法加上(i-2,j-1)的走法数
        			methods[i][j]%=1000000007;//每次更新methods的值时,都进行取余运算,如果全部运算完再做取余运算,数字容易溢出
    			}
    			if(i-1>=1&&j-2>=1){
    				methods[i][j]+=methods[i-1][j-2];//马从(i-1,j-2)跳到(i,j)
        			methods[i][j]%=1000000007; 
    			}
    			if(i-1>=1&&j+2<=m){
    				methods[i][j]+=methods[i-1][j+2];//马从(i-1,j+2)跳到(i,j)
        			methods[i][j]%=1000000007; 
    			}
    			if(i-2>=1&&j+1<=m){
    				methods[i][j]+=methods[i-2][j+1];//马从(i-2,j+1)跳到(i,j)
        			methods[i][j]%=1000000007; 
    			}
    		}
    	}
        //methods[n][m]表示马从(1,1)到(n,m)点的走法数
    	System.out.println(methods[n][m]);

    }
}

七、进击的青蛙

非线性dp,数据结构与算法(java版),算法,动态规划非线性dp,数据结构与算法(java版),算法,动态规划

分析:

  • 跟爬楼梯的题目类似,照着写,然后根据题目的具体要求再修改一下即可
  • BufferedReader类相对于 Scanner 类,这种方法会占用更少的内存,特别是在处理大量输入时。
  • 该题如果用Scanner,只能拿20分,内存超限
package no1_1;
import java.io.*;

public class Main {
    public static void main(String[] args) throws NumberFormatException, IOException {
    	BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    	int n = Integer.parseInt(reader.readLine());//读取一整行的字符串,然后转成int类型
        int MOD = 1000000007;
        long[] dp = new long[n+1];//dp数组先用来存石头,然后覆盖着存储到达该的方法数,(省点内存)
        for(int i = 1; i <= n; i++) {
            dp[i] = Integer.parseInt(reader.readLine());
        }
        //初始化
        dp[1] = (dp[1] == 0) ? 1 : 0;
        dp[2] = (dp[2] == 0) ? dp[1] + 1 : 0;
        dp[3] = (dp[3] == 0) ? dp[1] + dp[2] + 1 : 0;
        
        for (int i = 4; i <= n; i++) {
            if (dp[i] == 1) {//该点有石头,无法到达
                dp[i] = 0;
            } else {//每次更新dp的方法数时,都要取余
                dp[i] = (dp[i-1]% MOD + dp[i-2]% MOD + dp[i-3]% MOD) % MOD;
            }
        }
        if(dp[n]==0) {//无法到达
            System.out.println("No Way!");
        }else {//输出到达该点的方法数
            System.out.println(dp[n]);
        }
    }
}

 文章来源地址https://www.toymoban.com/news/detail-809063.html

到了这里,关于算法——动态规划(DP)——递推的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 猿创征文 |【算法面试入门必刷】动态规划-线性dp(一)

    📦个人主页:一二三o-0-O的博客 🏆技术方向:C/C++客户端资深工程师(直播+音视频剪辑) 👨‍💻作者简介:数据结构算法与音视频领域创作者 📒 系列专栏:牛客网面试必刷 📣专栏目标:帮助伙伴们通过系统训练,掌握数据结构与算法,收获心仪Offer 📝推荐一个找工作

    2024年02月03日
    浏览(63)
  • 猿创征文 |【算法面试入门必刷】动态规划-线性dp(四)

    📦个人主页:一二三o-0-O的博客 🏆技术方向:C/C++客户端资深工程师(直播+音视频剪辑) 👨‍💻作者简介:数据结构算法与音视频领域创作者 📒 系列专栏:牛客网面试必刷 📣专栏目标:帮助伙伴们通过系统训练,掌握数据结构与算法,收获心仪Offer 📝推荐一个找工作

    2024年02月02日
    浏览(35)
  • 数学建模十大算法03—线性规划、整数规划、非线性规划、多目标规划

    一、线性规划(Linear Programming,LP) 1.1 引例 在人们的生产实践中,经常会遇到 如何利用现有资源来安排生产,以取得最大经济效益的问题。 此类问题构成了运筹学的一个重要分支一数学规划,而 线性规划(Linear Programming, LP) 则是数学规划的一个重要分支。 简而言之,线

    2024年02月13日
    浏览(42)
  • 动态规划(DP)入门——线性DP

    在了解线性DP之前,我们首先要知道什么是动态规划,即为将一种复杂问题,分解成很多重叠的子问题,并通过子问题的解得到整个问题的解的算法。听起来比较抽象,动态规划简单来说就是确定问题的状态,通常题目都会提示,一般为“到第i个为止,xx为j(xx为k)的方案数/最

    2024年02月19日
    浏览(45)
  • 动态规划——线性DP

    暴力搜索:由可行的所有起点出发,搜索出所有的路径。 但是深搜的算法时间复杂度要达到 O ( 2 n ) O(2^n) O ( 2 n ) (每个数都有选或不选的两个选择),指数级的时间复杂度在本题中( n ≤ 100 n≤100 n ≤ 100 )显然是不能接受的。那么再观察这个这棵递归树,可以发现其中有很

    2024年01月19日
    浏览(46)
  • 动态规划:线性DP

    2024年02月06日
    浏览(40)
  • 数学建模整理-线性规划、整数规划、非线性规划

    在人们的生产实践中,经常会遇到如何利用现有资源来安排生产,以取得最大经济 效益的问题。若目标函数及约束条件均为线性函数,则称为线性规划(Linear Programming 简记 LP)。 可行解 :满足约束条件的解。 可行预 :所有可行解构成的集合称为问题的可行域,记为R。 图解法

    2024年02月06日
    浏览(36)
  • [动态规划]——线性DP(LIS/LCS/LCIS等) 详解

    线性DP,是较常见的一类动态规划问题,其是在线性结构上进行状态转移,这类问题不像背包问题、区间DP等有固定的模板 线性动态规划的目标函数为特定变量的线性函数,约束是这些变量的线性不等式或等式,目的是求目标函数的最大值或最小值 因此,除了少量问题(如:

    2024年02月10日
    浏览(45)
  • MATLAB 非线性规划

    ✅作者简介:人工智能专业本科在读,喜欢计算机与编程,写博客记录自己的学习历程。 🍎个人主页:小嗷犬的个人主页 🍊个人网站:小嗷犬的技术小站 🥭个人信条:为天地立心,为生民立命,为往圣继绝学,为万世开太平。 非线性规划问题 仍是规划问题的一种,但是

    2024年02月05日
    浏览(44)
  • 非线性规划

      非线性规划在工业界和学术界中应用非常普遍,譬如交通运输中的路径优化、金融领域中的资产配置、5G网络切片中VNF的放置等。很多时候,我们对复杂问题进行提炼和抽象后,发现可以建模成某一种非线性规划。然而,由于非线性规划多是NP难的问题,并不容易得到最优

    2023年04月08日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包