动态规划

由于动态规划解决的问题多数有重叠子问题这个特点,为减少重复计算,对每个子问题只解一次,将其不同阶段的不同状态保存在一个二维数组

斐波那契数列

1,1,2,3,5,8,13,21,34……这样的数列
在这里插入图片描述
递归算法:

//求第n个数的值
//递归算法
public static int fib(int n) {
	if(n==1||n==2) {
		return 1;
	}else {
		return fib(n-1)+fib(n-2);
	}
}

非递归算法:
递归算法在计算每一个n处的值时,都要向前递归,这时如果能将前面的值都保存起来就会更方便
非递归算法就是用一个数组将每一个数字保存起来,这样就不用在每次计算的时候,一个一个往回溯源,只要取这个数组中的值即可,避免了大量的重复计算

//非递归算法
public static int fib2(int n) {
	//因为F数组是从F[1]开始存的  所以这里要多存一个数 用n+1!!!
	int F[]=new int[n+1];
	if(n<0) {
		return -1;
	}
	F[1]=1;
	F[2]=1;
	//这里注意是i<=n不是i<n!!!
	for(int i=3;i<=n;i++) {
		F[i]=F[i-1]+F[i-2];
	}
	return F[n];
}

题目描述
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1)。
n<=39

public class Solution {
    public int Fibonacci(int n) {
    	//注意这里是n+2!!!为什么我还没想到 不应该是n+1吗
        int[] F=new int[n+2];
        F[0]=0;
        F[1]=1;
        for(int i=2;i<=n;i++){
            F[i]=F[i-1]+F[i-2];
        }
        return F[n];
    }
}

求不连续数字相加最大和-求最优解

比如求数组1,2,4,1,7,8,3中不连续的数字最大的和
分阶段,对第n个数字进行选还是不选
则有
在这里插入图片描述
这里的OPT(i)的公式就是我们的递归模型,代码根据这个来写
这时有两种情况:
OPT(0)=arr[0],OPT(1)=max{arr[0],arr[1]}

递归算法:

public static int max(int a,int b) {
	return a>b?a:b;
}

//求不连续的数字最大和
//递归算法
public static int opt(int[] arr,int i) {
	if(i==0) {
		return arr[0];
	}else if(i==1) {
		return max(arr[0],arr[1]);
	}else {
		int a=arr[i]+opt(arr, i-2);
		int b=opt(arr, i-1);
		return max(a, b);
	}
}

int arr[]= {1,2,4,1,7,8,3};
System.out.println(opt2(arr,arr.length-1));//15

非递归算法:

//非递归算法
public static int opt2(int[] arr,int i) {
	int optarr[]=new int[i+1];
	optarr[0]=arr[0];
	optarr[1]=max(arr[0], arr[1]);
	for(int j=2;j<optarr.length;j++) {
		int a=optarr[j-2]+arr[j];
		int b=optarr[j-1];
		optarr[j]=max(a, b);
	}
	return optarr[i];
}

int arr[]= {1,2,4,1,7,8,3};
System.out.println(opt2(arr,arr.length-1));//15

在这里是用一个数组来存储,当arr数组中有0个数,有1个数,有2个数,有3个数……时,其最优值。例如:
数组arr[]= {1,2,4,1,7,8,3}
optarr[0]=1
optarr[1]=2
optarr[2]=max{optarr[0]+arr[2],optarr[1]}=5
optarr[3]=max{optarr[1]+arr[3],optarr[2]}=5
optarr[4]=max{optarr[2]+arr[4],optarr[3]}=12
optarr[5]=max{optarr[3]+arr[5],optarr[4]}=13
optarr[6]=max{optarr[4]+arr[6],optarr[5]}=15

在一个数组中选择若干个数字相加组合等于指定的数字,若可以组合则返回true,若不可以则返回false

比如求数组[3,34,4,12,5,2]中是否有一个组合使其相加等于9,如果有则返回true,没有则返回false
在这里插入图片描述
递归模型为:
在这里插入图片描述

这里有三种情况:
当s==0时,例如subset(arr[2],0),说明已经有组合满足条件,返回true
当遇到subset(arr[0],s)时,这时候只剩下一个数字进行比较。如果arr[0]等于s,则可以组合,如果arr[0]不等于s,则不可以组合。return arr[0]=s
当arr[i]>s时,如果选arr[i]的话,此时在i的前面永远不能拼凑出值为s的组合,所以这时只考虑不选的情况,即return subset(arr,i-1,s)
递归算法:

//求数组中是否能拼凑出值s
//递归算法
public static boolean subset(int[] arr,int i,int s) {
	if(s==0) {
		return true;
	}else if(i==0) {
		return arr[i]==s;
	}else if(arr[i]>s) {
		return subset(arr, i-1, s);
	}else {
		boolean a=subset(arr, i-1, s-arr[i]);
		boolean b=subset(arr, i-1, s);
		//这里只要a和b有一个满足即可
		return a||b;
	}
}

int arr[]= {3,34,4,12,5,2};
System.out.println(subset(arr,5,9));//true
System.out.println(subset(arr,5,13));//false

非递归算法:
在这里插入图片描述
这里是用一个二维数组来存储过程中的值,最后一行最后一列的值就是最后的返回值,即subset(arr[i],9)

//非递归算法
public static boolean subset2(int[] arr,int S) {
	//先初始化一个二维数组,注意列数是s+1,因为是从0开始的
	Boolean[][] subset=new Boolean[arr.length][S+1];
	//初始化数组中的值
	//当s=0时,都为true
	for(int i=0;i<arr.length;i++) {
		subset[i][0]=true;
	}
	//当i=0时,若arr[0]=s则为true,否则为false
	for(int j=0;j<=S;j++) {
		subset[0][j]=false;
	}
	subset[0][arr[0]]=true;
	//其他情况
	for(int i=1;i<arr.length;i++) {
		for(int s=1;s<=S;s++) {
			//当arr[i]大于s则不选arr[i]
			if(arr[i]>s) {
				subset[i][s]=subset[i-1][s];
			}else {
				boolean a=subset[i-1][s-arr[i]];
				boolean b=subset[i-1][s];
				subset[i][s]=a||b;
			}
		}
	}
	return subset[arr.length-1][S];
}

剪绳子

给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1,m<=n),每段绳子的长度记为k[1],…,k[m]。请问k[1]x…xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
在这里插入图片描述
动态规划
大概思路是,先初始化,当绳长为2时,最大值为1;将绳子分为两部分,j和i-j。(j从1开始++)有两种情况,i和i-j都不再分割,则dp[i]=j*(i-j);i-j继续分割,则dp[i]=j*dp[i-j]。最后的值要取这之间的最大值

public class Solution {
    public int cutRope(int target) {
        if(target<=1){
            return -1;
        }
        int[] dp=new int[target+1];
        dp[2]=1;
        for(int i=3;i<=target;i++){
            for(int j=1;j<i;j++){
            	//状态转移方程 
            	//还没想明白为什么还要比较dp[i]
            	//应该是存在一种可能,绳子原来的长度比剪完几段相乘要大
                dp[i] = Math.max(Math.max(j*(i-j),j*dp[i-j]),dp[i]);
            }
        }
        return dp[target];
    }

}

数位dp

数位dp一般用来统计一个区间内满足一些条件数的个数。
本质上是优化正常数数即暴力枚举的过程。其实就是dfs+记忆化数组
首先要有一个数位处理函数,将输入的数进行分解
然后就是一个dfs函数,来执行数位dp

状态压缩(状压dp)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值