ZCMU 2112: 聪明的美食家【简单dp】【最长不降子序列】

4 篇文章 0 订阅
3 篇文章 0 订阅

ZCMU 2112: 聪明的美食家

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

思路

这题的样例很容易让第一次做这类题的人误解为计算前后数差值为正的数目,题目本意是在不走回头路的情况下取得最大的爽的次数,并且他拒绝“不爽”,意思为不允许出现美味度下降的情况,由于对美味度的高低没有要求,因此我们可以考虑在美味度逐渐增加的过程中跳过一两个美味度特别高的来避免不爽,由此题目便抽象为求最大不降子序列的长度。

分析状态,dp[i]表示该点所在的最长不降子序列的位置,状态转移方式为求出比i小或等于i的数中最大的数,将其dp加一得到i数的dp。一般想法为建立一个dp数组,存储每一点对应的位置,但是朴素做法的时间复杂度是N^2,在本题中超时。因此需要优化。

我们可以用一个容器来存储这些数,就拿样例来讲,当容器为空时,放入第一个数3,此时3在容器尾的第一位,代表3的dp值为1,之后我们对18处理,发现18的值大于3,而且3是比18小的数中最大的(当然,因为只有它),我们将18放入,此时18在第二位,代表18的dp值在3的dp值基础上加一,为2。接下来对7进行处理,发现7并不比18大,按照前述状态转移方式我们在容器中找小于等于7的最大数3(此时的查找方式为二分查找),将其dp值加一赋给当前数的dp,等价于将7放在3的上面。但是此时3上面已经有18了,怎么办?我们这么想,此时18和7的dp值均为2,而7比18小,在7后面接上更多数的几率更大,因此我们将18用7替换。用这种处理方式我们可以很快得出样例的最终结果是3 7 10 12 16 24,对其求长便得答案6。

其中的二分查找我们可以用库函数upper_bound(l, r, v)代替,其返回一个区间[ l, r ]中大于v的第一个数,注意还有一个函数是lower_bound(),参数与upper_bound相同,其返回的是第一个大于等于v的数,在本题中我们替换的是第一个大于当前值的数,并不替换等于当前值的数,因此用它会导致WA。

AC代码

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <cmath>
#include <string>
#include <list>
#include <algorithm>
#include <set>
#include <list>
#include <stack>
#include <map>
#include <bitset>
#define INF 0x3f3f3f3f
#define N 1000005
#define ll long long
 
using namespace std;
 
int num[200005];
int head;
 
int main()
{
    int t;
    while(~scanf("%d",&t))
    {
        head = 1;
        for (int i = 1; i <= t; i++)
        {
            int x;
            scanf("%d", &x);
            if (head == 1 || num[head - 1] <= x)
            {
                num[head] = x;
                head++;
            }
            else
            {
                //此处的二分可用库函数upper_bound()代替,但注意不可用lower_bownd()
                int min = 1, max = head - 1, mid;
                while(max>=min)
                {
                    mid = (max + min) / 2;
                    if (num[mid] > x)max = mid - 1;
                    else if (num[mid] <= x)min = mid + 1;
                }
                num[max+1] = x;
            }
        }
        printf("%d\n", head - 1);
    }
    return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值