混元算法 -------------动态规划

今天来说一个very牛逼的算法———动态规划。听名字就觉得很厉害,不错,它确实很厉害,很多问题只能用动态规划来解决。
那么什么是动态规划呢?
动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。20世纪50年代初,美国数学家贝尔曼(R.Bellman)等人在研究多阶段决策过程的优化问题时,提出了著名的最优化原理,从而创立了动态规划。动态规划的应用极其广泛,包括工程技术、经济、工业生产、军事以及自动化控制等领域,并在背包问题、生产经营问题、资金管理问题、资源分配问题、最短路径问题和复杂系统可靠性问题等中取得了显著的效果。
我们先说一个例子-------数字三角形,这个问题应该大部分人都见过吧,就是一个由1~100的数字搭建一个等腰三角形,从最顶层下到最底层,只能向左下或右下走,问经过的数字的最大值和是多少?最多有100层。这个问题其实用递归的话,思路很简单(对于基础稍微好点的),就是看它的最下面或者右下面那个到底层的路径之和最大,然后再加上它本身,从第一行开始。递归思路就是这,我就不过多阐述了,可以在网上搜。其代码如下:

#include<iostream>
#include<algorithm>
using namespace std;
#define Max 101

int N;
int D[Max][Max];

int MaxRose(int i, int j)
{
	if (i == N) return D[i][j];
	else
	{
		int x = MaxRose(i + 1, j);
		int y = MaxRose(i + 1, j + 1);
		return max(x,y)+D[i][j];
	}
}

int main()
{
	cin >> N;
	for (int i = 1; i <= N; i++)
	{
		for (int j = 1; j <= i; j++)
		{
			cin >> D[i][j];
		}
	}
	MaxRose(1, 1);
	cout << MaxRose(1, 1) << endl;
	return 0;
}

虽然怎么看起来简单,但是它的时间复杂度特别高,存在着大量的重复计算,比如说数组D[i][j]里面存的就是这个数字三角形,其中任意一个D[a][b]到底层的最大路径用到它一次就要调用一次这个函数,肯定越到中间越到下面的就被调用的越多,而且关键是调用过程是一模一样,经过计算,每一层要调用的函数都为2的n次方,一共n层那么一共要调用2的1次方一直加到2的n次方,假如一共有n层,那么它的时间复杂度就为2^n,100层的话就是2的100次方,汉诺塔问题2的64次方就已经不得了了,更别说这了,就算是计算机也很难算出来吧!所以这个问题不能只用递归,我们想一下,那个递归的方法存在大量的重复计算所以时间复杂度特别高,那么可以可以当它第一次算出来后就保存起来,下次可以直接调用,那么它就没有那么多的重复计算了。这样每个只用算一遍,那么它的时间复杂度就为n的平方,n最多为100,那么才10000,直接看代码:

#include<iostream>
#include<algorithm>
using namespace std;
#define Max 101

int N;
int D[Max][Max];
int maxrose[Max][Max];//这个数组就是用来存对应下标的数到底层的最大路径

int MaxRose(int i, int j)
{
	if (maxrose[i][j] != -1) return maxrose[i][j];
	if (i == N) return D[i][j];
	else
	{
		int x = MaxRose(i + 1, j);
		int y = MaxRose(i + 1, j + 1);
		maxrose[i][j] = max(x,y)+D[i][j];
	}
	return maxrose[i][j];
}

int main()
{
	cin >> N;
	for (int i = 1; i <= N; i++)
	{
		for (int j = 1; j <= i; j++)
		{
			cin >> D[i][j];
			maxrose[i][j] = -1;//初始化为-1,表示还没被算出来过。
		}
	}
	MaxRose(1, 1);
	cout << MaxRose(1, 1) << endl;
	return 0;
}

其实这个函数还有在空间上进行优化,我们没必要要用一个二维数组来存放maxrose[i][j];这样会有将近一般的空间是浪费的,我们可以直接就不用数组来存放maxrose[i][j],直接就在它本身的数组里面把原来的D[i][j]替换成maxsore[i][j],因为D[i][j]用一次之后就用不到了,还不如直接用来存放有用的,再优化一点,我们其实可以不用递归来写,我们直接用for循环来底层进行递推,这样让代码我们看起来更加的明了,好理解一点,而且其实这样代码也可以跑的更快一点,因为递归要一直在栈区开辟空间。代码如下:

#include<iostream>
#include<algorithm>
using namespace std;
#define Max 101

int N;
int D[Max][Max];


void MaxRose()
{
	for (int i = N; i >= 1; i--)
	{
		for (int j = 1; j <= i; j++)
		{
			if (i != N)
			{
				D[i][j] += max(D[i+1][j],D[i+1][j+1]);
			}
		}
	}
}

int main()
{
	cin >> N;
	for (int i = 1; i <= N; i++)
	{
		for (int j = 1; j <= i; j++)
		{
			cin >> D[i][j];
		}
	}
	MaxRose();
	cout << D[1][1] << endl;
	return 0;
}

这样是不是好多了呢?
用动态规划的一般解题思路:
1、要用到动态规划的问题,我们一般就会先想到用递归,它也是适用于可以把一个问题可以分解为若干个子问题,子问题和它本身的形式相同,只不过规模变小了,把所有子问题都解决了,那么这个大问题也就解决了,子问题一旦求出来一个就会被保存一个。
2、确定状态,状态就是要解决的每个子问题的过程(我是怎么理解的),比如数字三角形,它的状态就是看它左下或右下哪个到底层的路径大,就用哪个加上它本身代表它本身到底层的最大路径,状态值就是这个这个值。
3、确定一些初始状态和边界状态的值,然后用已知的推未知的。很显然数字三角形的初始状态就是最后一层的值,最后一层到最底层的最大路径肯定就是它本身,然后用它们推上一层的。
4、确定状态转移方程,这就是通过已知的推未知的的方程,显然数字三角形的状态方程为:在这里插入图片描述
能用动态规划的问题的特点:
1、具有最优子问题的结构性质,简单来说当题目问你……最……路径的问题可以首先考虑动态规划。
2、按我的话来说,就是最后的结果不会和每个子过程都有关,只是最优解的那若干个子系列有关,和其他的无关。

这就是我对动态规划的一些拙见,前面我也做了一些关于动态规划的题目,大家可以看看加深一下印象:
1、矩阵
2、最优包含
3、K倍区间
4、最长上升子序列

有哪不对可以评论区指出来哦!!!欢迎指正哈!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值