蓝桥杯 - 蓝桥骑士 动态规划和二分法做法

题目描述

小明是蓝桥王国的骑士,他喜欢不断突破自我。

这天蓝桥国王给他安排了 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

题解

这道题最简单的方法是使用动态规划法,直接套模板

扫描二维码关注公众号,回复: 14588560 查看本文章

但是由于时间复杂度是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;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_51712663/article/details/127309531