NOIP2013花匠超多方法的题解

原题见洛谷(https://www.luogu.org/problem/show?pid=1970)
首先介绍一种我在做题的时候学到的一种空间复杂度为O(1)的解法
首先使用f1,f2分别记录两种不同的序列。就是一种为两边高中间低和一种为中间高两边低的序列。f1,f2的初始值都设成1(我们可以易证选择了1第一盆其结果一定不会变差#手动滑稽)用x来记录上一盆,h来记录当前盆满足一下几个条件:
1.h==x时无需转移
2.h>x时可以转移其中一个
3.h

#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;

inline int read(){
    int num;
    char ch;
    while((ch=getchar())<'0' || ch>'9');
    num=ch-'0';
    while((ch=getchar())>='0' && ch<='9'){
        num=num*10+ch-'0';
    }
    return num;
}
inline void out(int x){
    if(x>=10){
        out(x/10);
    }
    putchar(x%10+'0');
}

int main(){
    int n,h,f1,f2,x;
    f1=1,f2=1;
    n=read();
    h=read();
    //scanf("%d%d",&n,&h);
    for(register int i=1;i<n;i++){
        x=h;
        h=read();
        //scanf("%d",&h);
        if(h>x){
            f1=max(f1,f2+1);
        }
        if(h<x){
            f2=max(f2,f1+1);
        }
    }
    out(max(f1,f2));
    //printf("%d",max(f1,f2));
    return 0;
}

然后介绍一种看题解看到的用DP做的题解:
首先分析这道题可以大致的总结为:求一个最长序列使得该序列的任意相邻的三个元素中间的那个元素不是最大的就是最小的,这样子我们连想到最长不下降子序列:比如元素d[i]和d[j],假设d[i]>d[j]且j>i,那么我们会让long_[j]=max(long_[j],long_[i]+1); 那么我们可以类比一下,最长不下降子序列是在当前节点前面找一个符合条件的,那么我们这道题也可以作为一道最长不下降子序列来做,只是当前节点寻找前驱的范围是前一个节点,而且我们还要用同样的方法求一个最长不上升子序列,最后取max,那么这道题就完了;
贴一篇偷悄悄抢来的代码:

#include<iostream>
#include<cstdio>
#define II int
#define R register
#define I 123456
using namespace std;

II a[I],d_1[I],d_2[I];

II n;

int main()
{
    scanf("%d",&n);
    R II x;
    for(R II i=1;i<=n;i++) scanf("%d",&x), a[i]=x;
    d_1[1]=d_2[1]=1;
    //d_1[]代表的是当前元素是以当前元素为中心的三个相邻的元素中最大的;
    //同理,d_2[]代表的是当前元素是以当前元素为中心的三个相邻的元素中最小的;
    //但是当前元素不一定选,可能是继承上一个元素的信息;
    for(R II i=2;i<=n;i++)
    {
        if(a[i]>a[i-1]) d_1[i]=max(d_1[i-1],d_2[i-1]+1), d_2[i]=d_2[i-1];
        //如果当前元素大于这个前一个元素;
        //那么当前元素若果选,则是前一个元素作为最低点时的长度+1;
        //如果不选,就继承前一个元素作为最高点,等价于当前元素作为最高点;
        //当前元素作为最低点就只能继承前一个点作为最低点;
            else{
                if(a[i]<a[i-1]) d_1[i]=d_1[i-1], d_2[i]=max(d_1[i-1]+1,d_2[i-1]);
                //如果当前元素小于前一个元素,则道理同上,只是反过来;
                    else d_1[i]=d_1[i-1],   d_2[i]=d_2[i-1];
                    //如果当前元素等于前一个元素,那么这个元素直接继承前一个元素的所有信息;
                    //因为这两个点是完全等价的;
            }
    }
    R II ans=max(d_1[n],d_2[n]);
    //我们在最后时取两种状态的最大值作为答案;
    cout<<ans<<endl;
    return 0;
}

本人亲测这段代码要16ms
再介绍一种是用贪心写的代码Orz,简单介绍一下思路吧:
先说说这个贪心的证明:
1.任意单调递增或递减的区间内不会有多于两株留下。若有三株留下则矛盾。(这个自己画个图就好了吧)
2.任意n个相连续的单调递增或递减的区间内不会有多于n+1株留下。
3.只有取每个最大单调区间两头的植株,才不会影响对下一个植株的选择(才可以取到最多)。
再粘一下贪心的代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
int a[100010];
void jyxin(int &x)
{
    char c=getchar();
    x=0;
    while(c>'9'||c<'0')c=getchar();
    while(c>='0'&&c<='9')x=x*10+c-48,c=getchar();
}
int main()
{
    int n,i=2,sum=0;
    jyxin(n);jyxin(a[1]);
    if(n==2)
    {
        jyxin(a[2]);
        if(a[2]==a[1])cout<<1;
        else cout<<2;
    }
    if(n==1)cout<<1;
    jyxin(a[2]);
    while(i<n)
    {
       i++;
       jyxin(a[i]);
       if(a[i]==a[i-1])a[i-1]=a[i-2];
       if((a[i]>a[i-1]&&a[i-2]>a[i-1])||(a[i]<a[i-1]&&a[i-2]<a[i-1]))sum++;
    }
    if(sum==0)
    {
        if(a[n]==a[n-1])cout<<1;
         else cout<<2;
    }
    else cout<<sum+2;
    return 0;    
}

大概这些就是这道题所有的解法了吧Orz

发布了41 篇原创文章 · 获赞 58 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/a1351937368/article/details/78104522