题目:http://www.sqyoj.club/problem.php?id=1044
分析:
以样例为例说明。
13 7 9 16 38 24 37 18 44 19 21 22 63 15
用a[1..14]记录关键字,
用b[i]记录以a[1]至a[i]的最长不下降子序列的长度,并初始化b[i]=1。
阶段、状态、决策(以顺推法说明):
第1阶段:
a[1]=13 a[2]=7。初始状态为b[1]=1,目标状态是计算b[2]。
因为a[1..1]中比a[2]小的没有,所以b[2]=1。
第2阶段:
a[1]=13 a[2]=7,a[3]=9。初始状态为b[1]=1,b[2]=1,目标状态是计算b[3]。
因为a[1..2]中比a[3]小的是a[2],所以b[3]=b[3]+b[2],从而b[3]=2。
第3阶段:
a[1]=13 a[2]=7,a[3]=9,a[4]=16。初始状态为b[1]=1,b[2]=1,b[3]=2,目标状态是计算b[4]。
因为a[1..3]中比a[4]小且b[]最大的是a[3],所以b[4]=b[4]+b[3],从而b[4]=3。
从而总结出:
第k阶段:
以b[1..k]为初始状态,目标状态是b[k+1]。
状态转移方程是
b[k+1]=b[k+1]+max{b[j] | a[j] < a[k+1] , 1<=j<=k }
满足最优子结构:计算出a[1]到a[k]的最长不降子序列后,肯定已经计算出了a[1]到a[i]的最长不降子序列(i=1,2,...,k-1)。
满足无后效性:当计算a[1]到a[k+1]的最长不降子序列长度,是以b[1..k]为初始状态,至于这些初始状态是怎样计算出来的,并不用关心,这就是未来与过去无关。
AC代码如下(逆推法):
#include<cstdio>
#include<iostream>
using namespace std;
int n,a[101][3];
int main(){
int n=1;
while(scanf("%d",&a[n][0])!=EOF){
a[n][1]=1;
a[n][2]=0;
n++;
}
n--;
for(int i=n-1;i>=1;i--){//阶段
int k=i,maxln=0;
for(int j=i+1;j<=n;j++)//状态
if(a[j][0]>a[i][0] && a[j][1]>maxln) {//决策
maxln=a[j][1];
k=j;
}
if(k!=i){
a[i][1]+=a[k][1];
a[i][2]=k;
}
}
int maxk=1;
for(int i=1;i<=n;i++)
if(a[i][1]>a[maxk][1])maxk=i;
cout<<"max="<<a[maxk][1]<<endl;
cout<<a[maxk][0];
int next=a[maxk][2];
while(next){
cout<<' '<<a[next][0];
next=a[next][2];
}
return 0;
}