问题 A: 最长上升子序列
时间限制: 2 Sec 内存限制: 64 MB
提交: 447 解决: 228
[提交][状态][讨论版][命题人:外部导入]
题目描述
一个数列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);
}
}