动态规划入门

一、基本思想

        一般来说,只要问题可以划分成规模更小的子问题,并且原问题的最优解中包含了子问题的最优解,则可以考虑用动态规划解决。动态规划的实质是分治思想和解决冗余,因此,动态规划是一种将问题实例分解为更小的、相似的子问题,并存储子问题的解而避免计算重复的子问题,以解决最优化问题的算法策略。
        由此可知,动态规划法与分治法和贪心法类似,它们都是将问题实例归纳为更小的、相似的子问题,并通过求解子问题产生一个全局最优解。
        其中贪心法的当前选择可能要依赖已经作出的所有选择,但不依赖于有待于做出的选择和子问题。因此贪心法自顶向下,一步一步地作出贪心选择;
        而分治法中的各个子问题是独立的 (即不包含公共的子子问题),因此一旦递归地求出各子问题的解后,便可自下而上地将子问题的解合并成问题的解。
但不足的是,如果当前选择可能要依赖子问题的解时,则难以通过局部的贪心策略达到全局最优解;如果各子问题是不独立的,则分治法要做许多不必要的工作,重复地解公共的子问题。解决上述问题的办法是利用动态规划。
        该方法主要应用于最优化问题,这类问题会有多种可能的解,每个解都有一个值,而动态规划找出其中最优(最大或最小)值的解。若存在若干个取最优值的解的话,它只取其中的一个。在求解过程中,该方法也是通过求解局部子问题的解达到全局最优解,但与分治法和贪心法不同的是,动态规划允许这些子问题不独立,也允许其通过自身子问题的解作出选择,该方法对每一个子问题只解一次,并将结果保存起来,避免每次碰到时都要重复计算。因此,动态规划法所针对的问题有一个显著的特征,即它所对应的子问题树中的子问题呈现大量的重复。动态规划法的关键就在于,对于重复出现的子问题,只在第一次遇到时加以求解,并把答案保存起来,让以后再遇到时直接引用,不必重新求解。
递推过程:
在这里插入图片描述

二、习题

2.1斐波那契数列

斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657
在这里插入图片描述
递归解法:

int Fib(int n)
{
	if (n == 0)
		return 0;
	else if (n == 1)
		return 1;
	else
		return Fib(n - 1) + Fib(n - 2);
}

递归+记忆法

vector<int> vc(20,-1);
int Fib2(int n)
{
	if (n == 0)
		return 0;
	else if (n == 1)
		return 1;
	else
	{
		if (vc[n] != -1)
		{
			return vc[n];
		}
		else
		{
			int nret = Fib2(n - 1) + Fib2(n - 2);
			int ntmp1;
			int ntmp2;
			if (vc[n-1] == -1)
			{
				ntmp1 = Fib2(n - 1);
				vc[n - 1] = ntmp1;
			}
			else
			{
				ntmp1 = vc[n - 1];
			}
			

			if (vc[n - 2] == -1)
			{
				ntmp2 = Fib2(n - 2);
				vc[n - 2] = ntmp1;
			}
			else
			{
				ntmp2 = vc[n - 2];
			}

			vc[n] = ntmp1 + ntmp2;
			return vc[n];
		}
	}
		
}

void main()
{
	int n = Fib1(6);
	cout << n << endl;
	n = Fib2(6);
	cout << n << endl;
	system("pause");
}

动态法:

int arry[100];
int Fib3(int n)
{
	arry[0] = 0;
	arry[1] = 1;
	for (int i = 2; i <= n;i++)
	{
		arry[i] = arry[i - 1] + arry[i - 2];
	}
	return arry[n];
}

刚开始大家可以按照 递归==》递归+记忆化==》递推,后面熟悉了就可以直接递推。对于这个题目可以说是最简单的动态规划了。

2.2爬楼梯

一个人爬楼梯,每次只能爬1个或两个台阶,假设有n个台阶,那么这个人有多少种不同的爬楼梯方法。
分析:
如果n1,显然只有从0->1一种方法f(1)=1;
如果n
2,那么有0->1->2、0->2两种方法f(2)=2;
如果n==3,那么可以先爬到第1阶,然后爬两个台阶,或者先爬到第二阶,然后爬一个台阶,显然f(3)=f(2)+f(1);
……
推广到一般情况,对于n(n>=3)个台阶,可以先爬到第n-1个台阶,然后再爬一个台阶,或者先爬到n-2个台阶,然后爬2个台阶,因此有f(n)=f(n-1)+f(n-2)。
那么动态规划的递推公式和边界条件都有了,即:
在这里插入图片描述
源码:

int dp(int stairs) 
{
	if (stairs == 1)
		return 1;
	int *dp = new int[stairs + 1];
	dp[1] = 1;
	dp[2] = 2;
	for (int i = 3; i <= stairs; ++i)
	{
		dp[i] = dp[i - 2] + dp[i - 1];
	}
	int nret = dp[stairs];
	delete[]dp;
	return nret;
}
3.3 整数分割

给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。
返回 你可以获得的最大乘积 。
示例 1:
输入: n = 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。
示例 2:
输入: n = 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。

说明: 你可以假设 n 不小于 2 且不大于 58。

#include<vector>
#include <algorithm>
using namespace std;

int max3(int a, int b, int c)
{
	return max(a, max(b, c));
}

int integerBreak(int n) {
	vector<int>  dp(n + 1,-1);
	for (int i = 2; i <= n; i++)//枚举需要拆分的数
	{
		dp[i] = i - 1;//初始化
		for (int j = 1; j < i - 1; j++)//枚举并更新动态数组
		{
			dp[i] = max3(j * (i - j), j * dp[i - j], dp[i]);
		}
	}
	return dp[n];
}



void main()
{
	cout << integerBreak(10) << endl;

	system("pause");
}

结果:
在这里插入图片描述

3.4 杨辉三角

给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
在这里插入图片描述
示例 1:

输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
示例 2:

输入: numRows = 1
输出: [[1]]

提示:

1 <= numRows <= 30

源码:

class Solution 
{
public:
	vector<vector<int>> generate(int numRows)
	{
		vector<vector<int>> ret(numRows);
		for (int i = 0; i < numRows; ++i) 
		{
			ret[i].resize(i + 1);
			ret[i][0] = ret[i][i] = 1;
			for (int j = 1; j < i; ++j) 
			{
				ret[i][j] = ret[i - 1][j] + ret[i - 1][j - 1];
			}
		}
		return ret;
	}
};
void main()
{
	Solution s;
	auto vc = s.generate(5);
	for (auto node :vc)
	{
		for (auto a:node)
		{
			cout << a << " ";
		}
		cout << endl;
	}
	system("pause");
}

结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
动态规划是一种用于解决一类具有重叠子问题和最优子结构性质问题的优化方法。它通过将问题分解为更小的子问题,并将子问题的解存储起来,以避免重复计算,从而提高算法的效率。 动态规划的基本步骤可以概括为三个:定义状态、设置状态转移方程和确定初始状态。 首先,我们需要定义问题的状态。状态是问题的不同阶段或步骤的某种表示。它可以是一个变量,也可以是一个数据结构。状态的定义需要根据问题的特性和要求进行设定。 接下来,我们需要设置状态转移方程。状态转移方程给出了问题从一个状态转移到另一个状态的规律。通过建立状态之间的联系,可以将问题分解为更小的子问题。这样,我们就可以通过已知子问题的解来求解更大的问题。 最后,我们需要确定初始状态。初始状态是问题最开始的状态。通过给出初始状态,可以递推得到后续的状态。 动态规划的核心思想是将问题分解为更小的子问题,并将子问题的解进行存储,以避免重复计算。这样做可以极大地减少计算量,提高算法的效率。 动态规划在解决一些经典问题中有着广泛的应用,比如最长递增子序列、0-1背包问题等。动态规划的理论和方法在计算机科学领域具有重要的意义,并且可以应用于其他领域,如经济学、生物学等。 总之,动态规划是一种重要的优化方法,可以有效地解决具有重叠子问题和最优子结构性质的问题。掌握动态规划的基本思想和方法对于解决复杂的问题具有重要的意义。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

发如雪-ty

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值