华为OJ训练题——最长递增子序列问题

本文介绍最长递增子序列(LIS)问题的两种应用场景,一是计算行走梅花桩的最大步数,二是计算调整为合唱队形所需的最少出列人数。通过动态规划方法实现,包括正向和逆向两次LIS计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最长递增子序列(longest increasing subsequence,LIS)的长度,计算LIS最常规的解法是动态规划法(时间复杂度为O(n^2) ),其思想为:

设max_len[i](初始值可全部设为1)表示arr中以arr[i]为末元素的最长递增子序列的长度,则有如下的递推思路:
在求以arr[i]为末元素的最长递增子序列时,在序号i前面找到所有满足能使arr[i]作为末元素的递增子序列要求的元素arr[j],即:

j<i,且arr[i]>arr[j]且(max_len[j]+1)>max_len[i];如果满足,则使max_len[i]=max_len[j]+1.最后在数组max_len[N]中找出最大值即为最长递增子序列的长度。

int LIS(int *arr, const int N) 
{  
  int* max_len = new int[N]; 
  max_len[0] = 1;
  int i;
  for(i=1; i<N; i++) 
  { 
    max_len[i] = 1;
    for(int j=0; j<i; j++) 
	{
		if(arr[i] > arr[j]) //如果求最长递减,则改为<
		{  
			max_len[i] =max(max_len[j]+1,max_len[i]);
		}
	}
	int result = 0;
	for(i=0; i<N; i++)
		result =max(max_len[i],result);
	delete[] max_len;
	return result;
  }
}
1、题目描述 
Redraiment是走梅花桩的高手。Redraiment总是起点不限,从前到后,往高的桩子走,但走的步数最多,不知道为什么?你能替Redraiment研究他最多走的步数吗? 
样例输入
6
2 5 1 5 4 5
样例输出

3

int LIS(int *arr, const int N) 
{  
  int* max_len = new int[N]; 
  max_len[0] = 1;
  int i;
  for(i=1; i<N; i++) 
  { 
    max_len[i] = 1;
    for(int j=0; j<i; j++) 
	{

		if(arr[i] > arr[j]) 
		{  
			max_len[i] =max(max_len[j]+1,max_len[i]);
		}
	}
	int result = 0;
	for(i=0; i<N; i++)
		result =max(max_len[i],result);
	delete[] max_len;
	return result;
  }
}


2、题目描述
计算最少出列多少位同学,使得剩下的同学排成合唱队形
说明:
N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。 
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK,   则他们的身高满足存在i(1<=i<=K)使得Ti<T2<......<Ti-1<Ti>Ti+1>......>TK。 
     你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。 
输入例子:
8
186 186 150 200 160 130 197 200
输出例子:

4

思路:两遍最长递增子序列,第一遍从左往右,第二遍从右往左,然后把两遍动态规划的结果相加,取最大的那个,比如比如{86 186 150 200 160 130 197 200},第一遍dp的结果是 1 1 1 2 2 1 3 4,第二遍dp的结果为3 3 2 3 2 1 1 1,那么相加最大是5,所以需要出列的同学个数就是8-5+1=4(为什么加1,中间的数多算了一次)。代码如下:

int my_solve(int *arr, const int N) 
{  
  int* max_len1 = new int[N]; 
  int* max_len2 = new int[N]; 
  //第一遍遍历
  max_len1[0] = 1;
  int i;
  for(i=1; i<N; i++) 
  { 
    max_len1[i] = 1;
    for(int j=0; j<i; j++) 
	{
		if((arr[i] > arr[j])) 
		{  
			max_len1[i] =max(max_len1[j]+1,max_len1[i]);
		}
    }
  }
  //第二遍遍历
  reverse(arr,arr+N);//先将arr进行翻转
  max_len2[0] = 1;
  for(i=1; i<N; i++) 
  { 
    max_len2[i] = 1;
    for(int j=0; j<i; j++) 
	{
		if((arr[i] > arr[j])) 
		{  
			max_len2[i] =max(max_len2[j]+1,max_len2[i]);
		}
    }
  }
  reverse(max_len2,max_len2+N);//将max_len进行翻转
  int result = 0;
  for(i=0; i<N; i++)
    result =max(max_len1[i]+max_len2[i],result);
  delete[] max_len1;
  delete[] max_len2;
  return result;
}

主函数代码:

#include<iostream>
#include<string>
#include<string.h>
#include<algorithm>
#include<vector>
#include<assert.h>
using namespace std;
#define max(a,b) ((a)>(b) ? (a):(b))
int LIS(int *arr, int N) ;
int my_solve(int *arr, const int N);
int main()
{
	int N;
	while(cin>>N)//输入数组的大小
	{
		int *data=new int[N];
		for(int i=0;i<N;i++)
			cin>>data[i];
		cout<<"最长递增子序列的长度为:"<<LIS(data,N)<<endl;
		cout<<"最少需要"<<N-my_solve(data,N)+1<<"位同学出列"<<endl;
	}
	return 0;
}




                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值