Description
如果有人认为吃东西只需要嘴巴,那就错了。
都知道舌头有这么一个特性,“由简入奢易,由奢如简难”(据好事者考究,此规律也适合许多其他情况)。具体而言,如果是甜食,当你吃的食物不如前面刚吃过的东西甜,就很不爽了。
大宝是一个聪明的美食家,当然深谙此道。一次他来到某小吃一条街,准备从街的一头吃到另一头。为了吃得爽,他大费周章,得到了各种食物的“美味度”。他拒绝不爽的经历,不走回头路而且还要爽歪歪(爽的次数尽量多)。
Input
两行数据。
第一行为一个整数n,表示小吃街上小吃的数量
第二行为n个整数,分别表示n种食物的“美味度”
Output
一个整数,表示吃得爽的次数
Sample Input
10 3 18 7 14 10 12 23 41 16 24
Sample Output
6
HINT
美味度为0到10000的整数
n<200000
分析:
完完全全一个最长不降子序列的模板题啊!找了一个模板,改了下数据范围,交上去就ac了,Σ(⊙▽⊙"a
最近中医药的题的难度是递增的,每次都有新的算法出现,而且是最简单的算法题的那种哈哈,那么这道题我的重点在于理解模板,理解算法。而且我又学会了一种除sort之外的stl。。。。upper-bound和lower-bound
对于upper_bound来说,返回的是被查序列中第一个大于查找值的指针,也就是返回指向被查值>查找值的最小指针,lower_bound则是返回的是被查序列中第一个大于等于查找值的指针,也就是返回指向被查值>=查找值的最小指针。
最长不降子序列详解:(两种做法,n^2和logn)
O(n²)解法,DP
我们设每一个数为阶段,因为没有后效性,可以用DP
设F[i]表示选到了第i个数,1~i中最长不下降子序列的长度是F[i]
怎么转移?我们只要从j=1~i-1枚举一个数,因为当前F[i]为必选,所以我们假设枚举的这个数是上一个最长不下降子序列的数。
当然,要满足a[i]≥a[j]
很明显,在可以选的情况下,越大越好
所以F[i]=max(F[j]) (i=1~n,j=1~i-1)
<span style="font-size:14px;">for i:=1 to n do
f[i]:=1;//一开始,F[i]=1,因为他们必选自己
for i:=2 to n do//第一个必定为1,所以从第二个开始寻找
begin
max:=0;
for j:=1 to i-1 do
if (a[i]>=a[j])and(f[j]>max) then//在前面寻找一个比他小(或者等于)的,一个最大的F[i]
max:=f[j];
f[i]:=max+f[i];
end;</span>
2.解决的问题:给定一个序列,求最长不下降子序列的长度(nlogn的算法没法求出具体的序列是什么)
定义:a[1..n]为原始序列,d[k]表示长度为k的不下降子序列末尾元素的最小值,len表示当前已知的最长子序列的长度。
初始化:d[1]=a[1]; len=1; (0个元素的时候特判一下)
考虑新进来一个元素a[i]:
如果这个元素大于等于d[len],直接让d[len+1]=a[i],然后len++。这个很好理解,当前最长的长度变成了len+1,而且d数组也添加了一个元素。
如果这个元素小于d[len]呢?说明它不能接在最后一个后面了。那我们就看一下它该接在谁后面。
准确的说,并不是接在谁后面。而是替换掉谁。因为它接在前面的谁后面都是没有意义的,再接也超不过最长的len,所以是替换掉别人。那么替换掉谁呢?就是替换掉那个最该被它替换的那个。也就是在d数组中第一个大于它的。第一个意味着前面的都小于等于它。假设第一个大于它的是d[j],说明d[1..j-1]都小于等于它,那么它完全可以接上d[j-1]然后生成一个长度为j的不下降子序列,而且这个子序列比当前的d[j]这个子序列更有潜力(因为这个数比d[j]小)。所以就替换掉它就行了,也就是d[j]=a[i]。其实这个位置也是它唯一能够替换的位置(前面的替了不满足d[k]最小值的定义,后面替换了不满足不下降序列)
至于第一个大于它的怎么找……STL upper_bound。每次复杂度logn。
后来我发现最长不降子序列是可以输出的!!!
总有不服输的博主(你说不能输出就不能输出啊)啊摔!
为什么这个方法是对的呢?倒序查找保证了两个条件:
- 如果 c 中有多个相同的数,后面的一定是最新更新的;
- 在保证一条件的前提下,倒序找,后面的数一定可以接到前面数的后面。”
具体实现:
//From - Milky Way
#include <cstdio>
#include <algorithm>
#include <stack>
using namespace std;
int d[100], c[100], a[100], len = 1;
int main() {
int n; scanf("%d", &n);
for (int i = 1; i <= n; ++ i) {
scanf("%d", &a[i]);
}
d[1] = a[1], c[1] = 1;
for (int i = 2; i <= n; ++ i) {
if (d[len] <= a[i]) {
d[++ len] = a[i], c[i] = len;
} else {
int j = upper_bound(d + 1, d + len + 1, a[i]) - d;
d[j] = a[i], c[i] = j;
}
}
stack<int> sta;
for (int i = n, j = len; i >= 1; -- i) {
if (c[i] == j) {
sta.push(a[i]); --j;
}
if (j == 0) break;
}
printf("%d\n", len);
while (!sta.empty()) {
printf("%d ", sta.top());
sta.pop();
}
return 0;
}
最长不下降之输出子序列 - NlogN
ac代码
#include<cstdio>
#include<algorithm>
using namespace std;
int a[400005];
int d[400005];
int main()
{
int n;
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
if (n==0) //0个元素特判一下
{
printf("0\n");
return 0;
}
d[1]=a[1]; //初始化
int len=1;
for (int i=2;i<=n;i++)
{
if (a[i]>=d[len]) d[++len]=a[i]; //如果可以接在len后面就接上,如果是最长上升子序列,这里变成>
else //否则就找一个最该替换的替换掉
{
int j=upper_bound(d+1,d+len+1,a[i])-d; //找到第一个大于它的d的下标,如果是最长上升子序列,这里变成lower_bound
d[j]=a[i];
}
}
printf("%d\n",len);
return 0;
}