系列文章目录
这代码的时间度可以发现非常大假设数组长度为n那么一共有2的n次方给子序列,而每一个子序列我们都需要去遍历一次。时间复杂度为o(n*2^n)非常满。
前言
帮助之间理解动态规划的一篇文章。
一、动态规划是什么?
1.是一中减少时间复杂度的算法。
2.避免了在计算中大量的重复计算。
3.算法本质上是利用了空间换时间思想。(后面会解释)
二、带入题目理解
1.题目
给你应该无序不重复数组nums={1,6,3,5,4},要求我们找出其中最长的递增的子序列。
2.思路1.
这里的1 3 4 和1 3 5是其中的答案。那么我应该怎么做,我先想到使用递归,从最初开始递归,比它大就进行函数调用,并且最大子序列加一,退出条件就是递归到最后。每次递归后在进行一个最大值的比较,代码实现。
代码
#include<bits/stdc++.h>
using namespace std;
int nums[]={1,6,3,5,4};
int n=5;
int mm(int m)//求当前数的最大子序列长度的函数
{
if(m==n-1)//到达最后子序列只有一
{
return 1;
}
int max_m=1;//最大子序列长度;
for(int j=m+1;j<n;j++)
{
if(nums[j]>nums[m])
{
max_m=max(max_m,mm(j)+1);
}
}
return max_m;
}
int main(){
int mmm=0;
for(int i=0;i<n;i++)
{
mmm=max(mmm,mm(i));
}
cout<<mmm;
}
总结
这分代码的时间度可以发现非常大假设数组长度为n那么一共有2的n次方给子序列,而每一个子序列我们都需要去遍历一次。时间复杂度为o(n*2^n)非常大。
思路二
那是不是又什么办法进行优化,观察模拟过程
我们可以发现在 我们在遍历1 3 4的时候就已经计算处理4的最大子序列长度,后面1 4的时候又进行了一次计算。可以发现我们进行大量的重复计算。那么应该如何处理。这里就用到了空间换时间的思想,我只需要开一共空间(mapp)记录已经计算过的答案,那么在遇到的时候直接进行调用就可以了,这也是动态规划的一般思路。
代码
#include<bits/stdc++.h>
using namespace std;
int nums[]={1,6,3,5,4};
int n=5;
int mapp[5]={0};
int mm(int m)//求当前数的最大子序列长度的函数
{
if(m==n-1)//到达最后子序列只有一
{
return 1;
}
if(mapp[m]!=0)
{
return mapp[m];
}
int max_m=1;//最大子序列长度;
for(int j=m+1;j<n;j++)
{
if(nums[j]>nums[m])
{
max_m=max(max_m,mm(j)+1);
}
}
mapp[m]=max_m;
return max_m;
}
int main(){
int mmm=0;
for(int i=0;i<n;i++)
{
mmm=max(mmm,mm(i));
}
cout<<mmm;
}
可以发现运行时间大大降低。
总结
通过将已经算出的答案进行记录,后面遇到了直接调用。
思路三
那么在尝试了递归,我们也可以尝试非递归。
从前面的介绍知道我们要计算从一开始的最长子序列递增长度,需要依次检查后面的所有数
的最长子序列递增长度可以得到 1 的最长子序列递增长度=max(后面的数的子序列递增长度)+1,检查从6开始的最长子序列递增长度需要依次计算后面的所有数.那我们可以得到。
最后一共长度为1;
可以发现我们只需要从后往前依次的计算,就能把所有答案推算出来
代码
#include<bits/stdc++.h>
using namespace std;
int main(){
int l[5]={1,1,1,1,1};
int nums[]={1,6,3,5,4};
for(int i=4;i>=0;i--)//从下往上计算
{
for(int j=i+1;j<=4;j++)//计算后面的数
{
if(nums[j]>nums[i])
{
l[i]=max(l[i],l[j]+1);
}
}
}
cout<<max(l);//输出l中最大的即可
}
这里举例了简单的案例,方便自己跟着模拟。
总结
这里的复杂度降到了(n^2),这就是最终的动态规划。