栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

试题 算法训练 小明爬山(最长公共子序列 Java)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

试题 算法训练 小明爬山(最长公共子序列 Java)

问题描述
  你有个同学叫小明,他早听闻祖国河山秀丽,于是有一个爬山的计划,并列了一张所有山的高度表,而又因“人往高处走”的说法,所以他希望爬的每一座山都比前一座要高,并且不能改变山的高度表上的顺序,爬的山数还要最多,请贵系的你帮他解决这个问题。
输入格式   
输入第一行为num,代表山的个数   
输入第二行有num个整数,代表每座山的高度
输出格式
  输出只有一个数,为符合要求的最大爬山数
样例输入
10
1 3 5 7 9 2 4 6 8 10
样例输出
6
样例输入
10
1 3 2 7 5 4 5 6 10 11
样例输出
7
数据规模和约定   
对于100%的数据,num <= 100000,高度<=10000。
题目链接:小明爬山

思路:
  可以很容易看出是求最长上升子序列的题,根据其思想去解决

求解:

  1. 方法一:使用动态规划的思想
    a:状态的表示: d p [ i ] dp[i] dp[i] 表示以第 i 个数结尾的最长上升子序列的长度
    b:状态转移:如果第 i 个数大于以第1~ i-1的某个数结尾的数 j,那么 d p [ i ] dp[i] dp[i] 就可以由 d p [ j ] dp[j] dp[j]得到,值为 d p [ j ] + 1 dp[j]+1 dp[j]+1

代码:

import java.util.Scanner;
public class Main {
	static int[] nums = new int[1000001];
	static int[] dp = new int[1000001];
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		for(int i = 1; i <= n; i++)
			nums[i] = sc.nextInt();
		dp[1] = 1;	//初始化
		int max = 1; //最长长度
		for (int i = 2; i <= n; i++) {
			dp[i] = 1;  //每一位数都可以由自身构成长度为1的子序列
			for (int j = 1; j < i; j++) {//更新长度
				if(nums[i] > nums[j])
					dp[i] = Math.max(dp[i], dp[j] + 1);
			}
			max = Math.max(max, dp[i]);
		}
		System.out.println(max);
	}
}

复杂度分析:
  两个for循环会达到 O ( n 2 ) O(n^2) O(n2),数据量大会超时,所以只能通过部分测试点。

  1. 方法二:利用序列自身特征去巧算,此方法需要一个辅助数组统计最长上升子序列的长度。
    a:数组 tmp[]临时存储山的高度,len统计tmp[]内的数据个数,high[]为原始序列。
    b:初始化: t m p [ 1 ] = h i g h [ 1 ] , l e n = 1 tmp[1] = high[1], len = 1 tmp[1]=high[1],len=1
    c:操作步骤:逐个处理high[]中的数字:当处理 h i g h [ k ] high[k] high[k] 时:
     ① h i g h [ k ] high[k] high[k] 比 t m p [ l e n ] tmp[len] tmp[len] 大,就将其加到tmp[]末尾,len + 1。
     ② h i g h [ k ] high[k] high[k] 比 t m p [ l e n ] tmp[len] tmp[len] 小,替换tmp[]中第一个大于等于它的数字

下面举个例
  high[ ] = {4,8,9,5,6,7}

这里说明一下为什么要替换第一个大于等于它的数字 ?
  因为后面可能有很多数都比 t m p [ l e n ] tmp[len] tmp[len] 小,而用这些数组成子序列可能会有更好的结果,我们用比 t m p [ l e n ] tmp[len] tmp[len] 小的 h i g h [ i ] high[i] high[i] 替换第一个大于等于它的数既并不会影响 l e n len len 的最终长度,也保证了后面小于 t m p [ l e n ] tmp[len] tmp[len] 的数也能去尝试构成结果序列的一部分。
  就像上面表格的第三行 4 , 8 , 9 4,8,9 4,8,9 来说,若小于 9 的 5 不替换大于等于它的第一个数,就得不到最终的结果序列 4 , 5 , 6 , 7 4,5,6,7 4,5,6,7。

代码:

import java.util.Scanner;

public class Main {
	static int[] high = new int[100001];	//存放山高度的数组
	static int[] tmp = new int[100001];		//辅助统计最长递增子序列的数组
	static int n;	//山的数量
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		for (int i = 1; i <= n; i++)
			high[i] = sc.nextInt();
		System.out.println(getLIS());
	}
	public static int getLIS() {	//获取最长长度
		int len = 1;
		tmp[1] = high[1];	//初始化
		for (int i = 2; i <= n; i++) {
			if(high[i] > tmp[len])	//符合递增条件,直接放入
				tmp[++len] = high[i];	
			else {	//不是递增的就替换第一个不小于它的数
				int j = findIndex(i, len);	//查找第一个不小于它的数
				tmp[j] = high[i];	//替换
			}
		}
		return len;
	}
	public static int findIndex(int k, int len) {//利用二分查找,降低复杂度
		int l = 1, r = len;
		int num = high[k], res = r;
		while(l < r) {
			int mid = (l + r) >> 1;
			if(tmp[mid] >= num) {//大于或等于就改变res和右区间
				res = mid;
				r = mid;
			}
			else 	//小于就改变左区间
				l = mid + 1;
		}
		return res;
	}
}

复杂度分析:for循环中二分,复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)。成功AC。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/975584.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号