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