1 问题分析
适合用动态规划算法解决的问题需要满足:拥有最优子结构并且问题无后效性这两个条件。接下来,笔者将分析本题是否满足上述两个条件:
1.1 最优子结构
对于第一个数,其最长不下降子序列是1(它自己)。求第i个数(i>1)的最长不下降子序列,我们可以先找满足不大于第i个数的数,之后在这些满足条件的数里面找到最长的序列的长度,加1(要把第i个数也加进去),就可以得到第i个数的最长不下降子序列的长度。这样看,第i(i>1)个数的最长不下降子序列可以从前i-1个数的最长不下降子序列得到答案,而且是第1个数也是有答案的,满足最优子结构。
1.2 无后效性
通过1.1的分析,我们知道第一个数的最长不下降子序列是1;从第二个数起,其最长不下降子序列都源于前面i-1个数的最长不下降子序列,故无后效性,可以使用动态规划求解。
2 解题思路
我们可以从前往后依次确定每个数的最长不下降子序列长度,并求和。
2.1 存储结构
为此我们可以开三个数组:a,f,v。a数组用来存储每个数的值;f数组存储每个数的最长不下降子序列的长度;v数组存储最长不下降子序列中所有数的和。
2.2 判断条件及执行过程
在这里有一点需要注意的是,题目说了“如有多个最长不下降子序列,那么取编号字典序最小的”,所以,1.在遍历前面的数的顺序时应该从前往后遍历;2.在判断条件上,我们除了要判断前面的数小于等于当前的数之外,还有判断前面的数的最长不下降子序列长度+1是否大于当前数原本的最长不下降子序列长度。如果是,更新当前数最长不下降子序列的长度与所有数的和,这个在f和v中更新相应的元素,否则就在看后一个数的情况。用这两个条件可以找到最长的不降子序列并且字典序最小。
2.3 f和v数组的初始化
抛开题目测试数据的样例,我们看一下,如果n=5,给的是5,4,3,2,1的话,最后应该输出5,4,3,2,1。若2.2的判断语句用单分支结构实现,那么每一次执行循环体不满足条件,所以f和v中相应的元素都没有变化,等于初始值。根据数据特点,我们可以把f中的元素初始化为1;将a中的元素初始化时赋给v数组。
3 AC代码
#include<bits/stdc++.h>
using namespace std;
int f[10001],a[10001];
int v[10001];
int n;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
f[i]=1;
v[i]=a[i];
}
for(int i=2;i<=n;i++){
for(int j=1;j<=i-1;j++){
if(a[j]<=a[i]&&f[i]<f[j]+1){
f[i] = f[j]+1;
v[i] = v[j]+a[i];
}
}
}
for(int i=1;i<=n;i++)
cout<<v[i]<<" ";
return 0;
}