题目描述
小明是蓝桥王国的骑士,他喜欢不断突破自我。
这天蓝桥国王给他安排了 NN 个对手,他们的战力值分别为 a_1,a_2,…,a_na1,a2,…,a**n,且按顺序阻挡在小明的前方。对于这些对手小明可以选择挑战,也可以选择避战。
身为高傲的骑士,小明从不走回头路,且只愿意挑战战力值越来越高的对手。
请你算算小明最多会挑战多少名对手。
输入描述
输入第一行包含一个整数 NN,表示对手的个数。
第二行包含 NN 个整数 a_1,a_2,…,a_na1,a2,…,a**n,分别表示对手的战力值。
1\leq N \leq 3\times10^51≤N≤3×105,1\leq a_i \leq 10^91≤a**i≤109。
输出描述
输出一行整数表示答案。
输入输出样例
示例 1
输入
6
1 4 2 2 5 6
输出
4
运行限制
- 最大运行时间:1s
- 最大运行内存: 128M
题解
这道题最简单的方法是使用动态规划法,直接套模板
但是由于时间复杂度是O(n^2),所以会超时
public static int lengthOfLIS_dp(int[] num){
if(num.length == 0){
return 0;
}
//构建一个最长上升子序列
//dp[i]表示到[0,i]之间的最长子序列长度
int[] dp = new int[num.length];
//初始值设置为1
for(int i = 0; i < dp.length; i++){
dp[i] = 1;
}
int result = 0;
for(int i = 0; i < dp.length; i++){
//内循环比较num[i]与其之前的值,进行更新
for(int j = 0; j < i; j++){
if(num[i] > num[j]){
dp[i] = Math.max(dp[i],dp[j] + 1);
}
if(dp[i] > result) result = dp[i];
}
}
return result;
}
另一种方法是使用二分法+贪心
时间复杂度是O(nlong)
核心数据结构是tails数组
tails[i]表示连续长度为(i+1)的连续子序列中最小的末尾元素
public static int lengthOfLIS_binary(int[] nums){
if(nums.length == 0){
return 0;
}
//维护一个tails数组 tails[i]表示连续长度为(i+1)的连续子序列中最小的末尾元素
//tail数组是递增的 这样才可以使用二分查找查入元素更新
int res = 0;
int tails[] = new int[nums.length];
for(int num: nums){
int i = 0, j = res;
//左闭右开
while(i < j){
int mid = (i + j) / 2;
if(tails[mid] < num){
i = mid + 1;
}else{
j = mid;
}
}
tails[i] = num;
if(j == res){
res++;
}
}
return res;
}
代码
import java.util.Scanner;
import java.io.*;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
public static void main(String[] args) throws IOException{
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
in.nextToken();
int N = (int)in.nval;
int[] num = new int[N];
for(int i = 0; i < N; i++){
in.nextToken();
num[i] = (int)in.nval;
}
System.out.println(lengthOfLIS_binary(num));
}
//dp做法
//时间复杂度O(n^2)在oj里会超时
public static int lengthOfLIS_dp(int[] num){
if(num.length == 0){
return 0;
}
//构建一个最长上升子序列
int[] dp = new int[num.length];
for(int i = 0; i < dp.length; i++){
dp[i] = 1;
}
int result = 0;
for(int i = 0; i < dp.length; i++){
for(int j = 0; j < i; j++){
if(num[i] > num[j]){
dp[i] = Math.max(dp[i],dp[j] + 1);
}
if(dp[i] > result) result = dp[i];
}
}
return result;
}
//使用二分+贪心
//时间复杂度是O(nlogn)
public static int lengthOfLIS_binary(int[] nums){
if(nums.length == 0){
return 0;
}
//维护一个tails数组 tails[i]表示连续长度为(i+1)的连续子序列中最小的末尾元素
//tail数组是递增的 这样才可以使用二分查找查入元素更新
int res = 0;
int tails[] = new int[nums.length];
for(int num: nums){
int i = 0, j = res;
while(i < j){
int mid = (i + j) / 2;
if(tails[mid] < num){
i = mid + 1;
}else{
j = mid;
}
}
tails[i] = num;
if(j == res){
res++;
}
}
return res;
}
}