题目描述
一个数列ai如果满足条件a1 < a2 < ... < aN,那么它是一个有序的上升数列。我们取数列(a1, a2, ..., aN)的任一子序列(ai1, ai2, ..., aiK)使得1 <= i1 < i2< ... < iK <= N。例如,数列(1, 7, 3, 5, 9, 4, 8)的有序上升子序列,像(1, 7), (3, 4, 8)和许多其他的子序列。在所有的子序列中,最长的上升子序列的长度是4,如(1, 3, 5, 8)。

    现在你要写一个程序,从给出的数列中找到它的最长上升子序列。

输入
输入包含两行,第一行只有一个整数N(1 <= N <= 1000),表示数列的长度。

第二行有N个自然数ai,0 <= ai <= 10000,两个数之间用空格隔开。

输出
输出只有一行,包含一个整数,表示最长上升子序列的长度。

样例输入
7
1 7 3 5 9 4 8
样例输出
4

思路

本题是一道动态规划问题,如果暴力求解的话,每一个数都有选或者不选两种状态,然后判断是否为上升子序列,如果是,就更新最长长度,直到枚举完所有情况。但是,当有n个元素的时候,其复杂度将达到O(2^n),这显然是不可承受的。

所以利用动态规划可以显著的降低复杂度。

令dp[i]表示以a[i]结尾的最长上升子序列的长度,对a[i]来说有两种可能:

1)如果在i之前存在比a[i]小的数a[j](j < i),并且dp[j] + 1  > dp[i](即把a[i]放到以a[j]结尾的子序列之后其长度大于当前以a[i]结尾的子序列的长度),那么就把a[i]放到之前以a[j]结尾的子序列之后,并令其长度+1(即dp[i] = dp[j] + 1);

2)如果a[i]之前的所有数都比它大,那么只能a[i]自身成一个子序列,其长度为1。

import java.util.Scanner;
 
public class Main{
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in); 
		int n = sc.nextInt();
		int[] arr = new int[10002];//arr数组表示输入的序列
		int[] dp = new int[10002];//dp数组中存放上升序列的长度,dp[i]表示以arr[i]结尾的子序列的最大长度
		for(int i = 1;i <= n;i++) {//输入序列
			int a = sc.nextInt();
			arr[i] = a;
		}
		
		int result = -1;//记录dp中最大的值
		
		for(int i = 1;i <= n;i++) {//按顺序计算dp[i]的值
			dp[i] = 1;//假设该子序列中只有arr[i],故长度为1,即其自身成为一个子序列
			for(int j = 1;j < i;j++) {
//如果在i之前有比arr[i]小的数(arr[j]),并且把该数(arr[i])放到以arr[j]结尾的子序列末尾后,
//其长度比当前以arr[i]结尾的子序列长度要长
				if(arr[i] > arr[j] && dp[j] + 1 > dp[i]) {
					dp[i] = dp[j] + 1;//把arr[i]放到以arr[j]结尾的子序列之后,原来的长度+1
				}
			}
			result = Math.max(result, dp[i]);//找出在dp数组中最大的一个,即子序列长度最长的一个
		}
		System.out.println(result);
	}
}