花匠

【题目描述】

花匠栋栋种了一排花,每株花都有自己的高度。花儿越长越大,也越来越挤。栋栋决定把这排中的一部分花移走,将剩下的留在原地,使得剩下的花能有空间长大,同时,栋栋希望剩下的花排列得比较别致。
具体而言,栋栋的花的高度可以看成一列整数H1,H2,…,Hn。设当一部分花被移走后,剩下的花的高度依次为G1,G2,…,Gm,则栋栋希望下面两个条件中至少有一个满足:
条件A:对于所有的1< i <m/2,G2*i > G2*i-1,且G2*i > G2*i+1; 
条件B:对于所有的1< i <m/2,G2*i < G2*i-1,且G2*i < G2*i+1。
注意上面两个条件在m = 1时同时满足,当m > 1时最多有一个能满足。
请问,栋栋最多能将多少株花留在原地。

【输入描述】

输入的第一行包含一个整数n,表示开始时花的株数。
第二行包含n个整数,依次为H1,H2,…,Hn,表示每株花的高度。

【输出描述】

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

【样例输入】


5 3 2 1 2

【样例输出】

3

【数据范围及提示】

对于 20%的数据,n ≤ 10; 
对于 30%的数据,n ≤ 25; 
对于 70%的数据,n ≤ 1000,0 ≤ Hi ≤ 1000; 
对于 100%的数据,1 ≤ n ≤ 100,000,0 ≤ Hi ≤ 1,000,000。

解法一:

抖动序列DP:O(n^2)

源代码:

#include<cstdio>
#include<algorithm>
using namespace std;
int n,i[100001],f[100001][2]={0}; //f[i][0]代表小大小,f[i][1]代表大小大。
int main() //自惭形秽的O(n^2)DP。
{
    scanf("%d",&n);
    for (int a=1;a<=n;a++)
      scanf("%d",&i[a]);
    f[1][1]=f[1][0]=1; //边界。
    for (int a=2;a<=n;a++)
    {
        int t1(0),t2(0); //t1代表降序到达i[b]的最大值,t2代表升序到达i[b]的最小值。
          for (int b=1;b<a;b++) //取最大值。
          {
              if (f[b][0]>t1&&i[b]>i[a])
                  t1=f[b][0];
              if (f[b][1]>t2&&i[b]<i[a])
                t2=f[b][1];
          }
          f[a][1]+=t1+1; //升序到达了i[a]。
          f[a][0]+=t2+1; //降序到达了i[a]。
    }
    printf("%d",max(f[n][0],f[n][1])); //输出最大值。
    return 0;
}

优化后:O(n)

源代码:

#include<cstdio>
#include<algorithm>
using namespace std;
int n,i[100001],f[100001][2]={0};
int main() //转换思想的O(n)DP。
{
    scanf("%d",&n);
    for (int a=1;a<=n;a++)
      scanf("%d",&i[a]);
    f[1][0]=f[1][1]=1; //边界。
    for (int a=2;a<=n;a++) //依次传值,f[i][0]表示前i株花降序到达的最大值,f[i][1]为与此同类。
    {
        if (i[a]<i[a-1]) //可降序到达。
        {
            f[a][0]=max(f[a-1][1]+1,f[a-1][0]); //选不选此株花。
            f[a][1]=f[a-1][1];
        }
        if (i[a]==i[a-1]) //纯传值。
        {
            f[a][0]=f[a-1][0];
            f[a][1]=f[a-1][1];
        }
        if (i[a]>i[a-1]) //可升序到达。
        {
            f[a][1]=max(f[a-1][0]+1,f[a-1][1]);
            f[a][0]=f[a-1][0];
        }
    }
    printf("%d",max(f[n][0],f[n][1])); //输出最大值。
    return 0;
}

解法二:

模拟解决:

源代码:

#include<cstdio>
int n,ans=2,i[100001]; //ans一开始便包括了始点和终点。
bool s;
int main()
{
    scanf("%d",&n);
    for (int a=1;a<=n;a++)
      scanf("%d",&i[a]);
    s=i[1]>i[2]; //模拟的思想为:可以将题意转化为求转折点的最多个数,再通过判断升降序列即可求解。
    for (int a=3;a<=n;a++)
    {
        if (s&&i[a]>i[a-1]) //降序转折。
        {
            ans++;
            s=0;
        }
        if (!s&&i[a]<i[a-1]) //升序转折。
        {
            ans++;
            s=1;
        }
    }
    printf("%d",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/Ackermann/p/5558073.html