[NOIP2013 提高组] 花匠

题目背景

NOIP2013 提高组 D2T2

题目描述

花匠栋栋种了一排花,每株花都有自己的高度。花儿越长越大,也越来越挤。栋栋决定把这排中的一部分花移走,将剩下的留在原地,使得剩下的花能有空间长大,同时,栋栋希望剩下的花排列得比较别致。

具体而言,栋栋的花的高度可以看成一列整数 h1,h2,…,hnh1​,h2​,…,hn​。设当一部分花被移走后,剩下的花的高度依次为 g1,g2,…,gmg1​,g2​,…,gm​,则栋栋希望下面两个条件中至少有一个满足:

条件 A:对于所有的 1≤i≤m21≤i≤2m​,有 g2i>g2i−1,同时对于所有的 1≤i≤m21≤i≤2m​,有 g2i>g2i+1;
条件 B:对于所有的 1≤i≤m21≤i≤2m​,有 g2i<g2i−1​,同时对于所有的 1≤i≤m21≤i≤2m​,有 g2i<g2i+1。

注意上面两个条件在 m=1m=1 时同时满足,当 m>1 时最多有一个能满足。

请问,栋栋最多能将多少株花留在原地。

输入格式

第一行包含一个整数 n,表示开始时花的株数。

第二行包含 n 个整数,依次为 h1,h2,…,hn,表示每株花的高度。

输出格式

输出一行,包含一个整数,表示最多能留在原地的花的株数。

输入输出样例

输入 #1

5
5 3 2 1 2

输出 #1

3

第一种解法——巧妙的DP大法


通过观察题面可以发现递推关系

于是可以用两个数组分别记录到i下降和到i上升

容易得出递推公式:

if(a[i]>a[i-1])f[i][0]=f[i-1][1]+1;
else f[i][0]=f[i-1][0];
if(a[i]<a[i-1])f[i][1]=f[i-1][0]+1;
else f[i][1]=f[i-1][1];

代码君奉上

#include<bits/stdc++.h>
using namespace std;
#define N 100010
int a[N],f[N][2]={0};
int n,tmp=0;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    f[1][0]=f[1][1]=1;
    for(int i=2;i<=n;i++){
        if(a[i]>a[i-1])f[i][0]=f[i-1][1]+1;
        else f[i][0]=f[i-1][0];
        if(a[i]<a[i-1])f[i][1]=f[i-1][0]+1;
        else f[i][1]=f[i-1][1];
    }
    cout<<max(f[n][0],f[n][1]);
    return 0;
}

第二种解法——更巧妙的贪心(Orz)

假设有N朵花

大致根据我们学过的函数思想,我们可以知道,存在m个g(2i)点在波峰或波谷上。

因为在波峰到波谷或者从波谷到波峰上的点单调,所以不满足这个性质,我们决定舍去这些点。

那么对于什么样的点留下呢,我们可以取那些波峰和波谷。

我们可以打标记,用flag表示。再定义一个计数的ans。

我们首先肯定要取1点,并且1点肯定是一个波峰或波谷(这个很重要)

所以我们先判断一下hi[1]和hi[2]的关系(hi [ ]表示高度)

如果hi[1]<hi[2]  说明1是波谷,那么我们标记flag=-1

如果hi[1]>hi[2] 说明1是波峰,那么我们标记flag=1

所以我们只需要判断是否hi[i]乘以flag > h[i+1]乘以flag

如果是的话 ,那么ans++;

否则说明状态改变了,那么就让标记flag*=-1

通过一个for循环,我们就求出了ans的值

所以我们只需输出:(N-ans)

好,下面我们来看代码(时间复杂度O(N))

#include <iostream>
using namespace std;
int main()
{
    int N;
    int hi[1000001]={0};
    int ans=0;
    cin>>N;
    for(int i=1;i<=N;i++)
    {
        cin>>hi[i];
    }
    int flag;
    if(hi[1]>hi[2]) flag=1;
    else flag=-1;
    for(int i=2;i<=N-1;i++)
    {
    if(hi[i]*flag>=hi[i+1]*flag) ans++;
    else flag*=-1; }
    cout<<N-ans;
}

在百忙之中写一篇题解也比较辛苦,别忘了点个赞!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值