这几天相当煎熬,动态规划实在是太难了,感觉最难的就是抽象递归,和贪心stl不同,dp代码的关键位置是看不懂的,看十几遍也整不动,以前最起码可以根据代码理清思路,现在只有将代码实现过程写到纸上手动跑才能勉强搞懂。
一个最长上升子段和问题,整整一天,在纸上写了一遍又一遍才把递归过程搞懂,现在说一下具体过程。
问题不再赘述,直接举例,给一个数列如下
1 7 2 8 3 4
那么它的最长上升子序列就是
1 2 3 4
长度为4,所以这个数列的最长子序列长度就是4。
现在来展示最核心的递归过程(图片出自知乎Mingqi)
这里我们可以看到,LIS(K+1)要么等于LIS(K),要么加了一。其实也很好理解,基本上就是,在前面所有的LIS种找到一个最长的LIS(i),如果A(K)比这个找到LIS(i)的尾项A(i)要大,则LIS(K)=LIS(i)+1,否则LIS(K)=LIS(i)。
代码实现如下
#include <iostream>
using namespace std;
int a[10001],maxlen[10001];
int main()
{
int n;
cin>>n;
for(int i = 1;i<=n;i++)
cin>>a[i];
maxlen[1] = 1;
for(int i = 2;i<=n;i++)
{
int temp = 0;//记录最长子序列的最后一个元素
for(int j = 1;j<i;j++)
//遍历i之前的所有元素,如果i大于j,那么子序列累加
{
if(a[i]>a[j])
{
if(maxlen[j]>temp)
temp = maxlen[j];
}
}
maxlen[i] = temp+1;
//加1是包含a[i]本身
}
int amax = -1;
for(int i = 1;i<=n;i++)
amax = max(amax,maxlen[i]);
cout<<amax<<endl;
return 0;
}
由这道题可以看出,dp最关键的就是递归的形式,也就是状态转移,一旦搞懂,豁然开朗!
即使越学越绝望也不能放弃,就要和dp肝到底,一天整不懂就两天,一道题不行就两道三道,积累量变,总能质变。