【蓝桥】 算法提高 聪明的美食家(最长上升子序列,upper_bound( ))

问题描述
  如果有人认为吃东西只需要嘴巴,那就错了。
  都知道舌头有这么一个特性,“由简入奢易,由奢如简难”(据好事者考究,此规律也适合许多其他情况)。具体而言,如果是甜食,当你吃的食物不如前面刚吃过的东西甜,就很不爽了。
  大宝是一个聪明的美食家,当然深谙此道。一次他来到某小吃一条街,准备从街的一头吃到另一头。为了吃得爽,他大费周章,得到了各种食物的“美味度”。他拒绝不爽的经历,不走回头路而且还要爽歪歪(爽的次数尽量多)。
输入格式
  两行数据。
  第一行为一个整数n,表示小吃街上小吃的数量
  第二行为n个整数,分别表示n种食物的“美味度”
输出格式
  一个整数,表示吃得爽的次数

样例输入 10 3 18 7 14 10 12 23 41 16 24 样例输出 6

数据规模和约定
  美味度为0到100的整数 n<1000
  //这里题目有错,实际给的数据远超1000,数组大小应该大一些,如1e+100

思路:
这里给两种解法,dp和二分。
1.dp:两层循环,找到原序列a[i]种各个元素作为结尾的最长子序列,长度用b[i]记录,找到b[i]中最大值,即为所求。O(n^2)。

2.二分(折半):这个要结合代码和网上资料好好理解下,大体就是,遍历一遍原序列,找到符合要求的元素放入非递减子序列b[i]的适当位置。(若a[i]>=b[i],b[i]长度+1;a[i]<b[i],找到b[i]中第一个大于a[i]的位置,用当前a[i]替换)。O(nlog2n)。

知识点:
1.最长上升子序列(longest increasing subsequence):
最长非递减子序列道理和LIS差不多,知识点参考链接:
https://www.cnblogs.com/frankchenfu/p/7107019.html

2.upper_bound( )二分函数的使用,初步掌握参考链接:https://blog.csdn.net/qq_40160605/article/details/80150252
主要注意:
lower_bound( begin,end,num)是查找第一个大于或等于num的函数,upper_bound( begin,end,num):查找第一个大于num的函数。
返回的地址减去起始地址begin,得到找到元素在数组中的下标。

3.*max_element(b,b+n)函数的又一次使用
注意返回的是地址,显示该位置的值需要加 “ * ”

AC代码:
1.DP方法

#include <bits/stdc++.h>
using namespace std;
int a[1010],b[1010];//原序列  存放以a[i]结尾的子序列最长值 
int main()
{
	int n; cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
		b[i]=1;
	 } 
	 for(int i=1;i<n;i++)//挨个遍历原序列,找到以a[i]作最后一个数的最长子序列 
	 {
	 	for(int j=0;j<i;j++)//将a[i]前的所有元素遍历一遍 
	 	//当然也可以这么写  for(int j=i-1; j>=0; j--) 
	 	{
	 		if(a[j]<=a[i])//有无"="可判断是否严格单调递增
			  {
			  	b[i]=max(b[i],b[j]+1);
			  } 
		 }
	 }
	 cout<<*max_element(b,b+n);//找数组最大元素函数,头文件为#include <algorithm> 
}

2.手写二分解法:

#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=101000;
int a[maxn],b[maxn];//a为原始数据,b为非递减子序列 

int his(int key,int low,int high)
{
	while(low<=high)
	{
		int mid=(low+high)/2;
		if(key>=b[mid]&&key<b[mid+1])
			return mid+1;
		else if(key>=b[mid])
		low=mid+1;
		else
		high=mid-1;
	}
}
int lis(int n)
{
	b[0]=a[0];
	int len=0,j;
	for(int i=1;i<n;i++)
	{
		if(b[len]<=a[i])
			j=++len;
		else
			j=his(a[i],0,len);//half interval search
		b[j]=a[i];
	}
	cout<<len+1;
}
int main()
{
	int n;
	cin>>n;
	for(int i=0;i<n;i++)
		cin>>a[i];
	lis(n);//longest increasing subsequence
	 
	return 0;
}

3.调用upper_bound()函数二分解法:

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
int a[maxn],b[maxn];
int main()
{
	int n,len=0;
	cin>>n;
	for(int i=0;i<n;i++)
		cin>>a[i];
	b[len++]=a[0];
	for(int i=1;i<n;i++)
	{
		int j=upper_bound(b,b+len,a[i])-b;
		if(j==len)
			b[len++]=a[i];
		else
			b[j]=a[i];
	}
	cout<<len;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41856733/article/details/88076254
今日推荐