NOIP2013 花匠 dp/贪心

来源:互联网 发布:旅行商问题算法matlab 编辑:程序博客网 时间:2024/05/29 12:50

【问题描述】

——————————————————————————————————————————

类似于单调子序列问题,不难发现可以dp。
f(i)表示以i结尾能够满足条件1的最多的花的数量
f(i)=max{ f(j) | f(j)%2==0 && h[j] > h[i] || f(j)%2==1 && h[j] < h[i]}+1
g(i)表示以i结尾能够满足条件2的最多的花的数量
g(i)=max{ g(j) | g(j)%2==0 && h[j] < h[i] || g(j)%2==1 && h[j] > h[i] } + 1
f(1)=g(1)=1;
线段树维护花的高度,可以离散化,分别对于两个要求建上两颗线段树,每个线段树维护两个数组,分别是f(i)%2==0的最大值和f(i)%2==1的最大值。

还算是比较水了,是半道代码题?(表述十分滑稽)

AC代码(dp):

#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<algorithm>#include<cmath>#include<queue>#include<set>#include<map>#include<cctype>#include<vector>#include<ctime>using namespace std;const int maxn=100005;int N,h[maxn];int f[maxn],g[maxn];int a[maxn],rank[maxn],MAX;int rt,lc[maxn<<1],rc[maxn<<1],maxv0[maxn<<1],maxv1[maxn<<1],np;void _scanf(int &x){    x=0;    char ch=getchar();    while(ch<'0'||ch>'9') ch=getchar();    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();}void data_in(){    _scanf(N);    for(int i=1;i<=N;i++) _scanf(h[i]);}void ready(){    memcpy(a,h,sizeof(a));    sort(a+1,a+N+1);    int cnt=unique(a+1,a+N+1)-a;    for(int i=1;i<=N;i++)        rank[i]=lower_bound(a+1,a+cnt+1,h[i])-a;    memcpy(h,rank,sizeof(h));    for(int i=1;i<=N;i++)        MAX=max(MAX,h[i]);}void set_segtree(int &now,int L,int R){    now=++np;    if(L==R) return;    int m=L+R>>1;    set_segtree(lc[now],L,m);    set_segtree(rc[now],m+1,R);}void update(int now,int L,int R,int x,int v){    if(L==R && L==x)    {        if(v%2==0) maxv0[now]=v;        else maxv1[now]=v;        return;    }    int m=L+R>>1;    if(x<=m) update(lc[now],L,m,x,v);    else update(rc[now],m+1,R,x,v);    if(v%2==0) maxv0[now]=max(maxv0[lc[now]],maxv0[rc[now]]);    if(v%2==1) maxv1[now]=max(maxv1[lc[now]],maxv1[rc[now]]);}int query0(int now,int L,int R,int x,int y){    if(x<=L && R<=y) return maxv0[now];    int m=L+R>>1;    if(y<=m) return query0(lc[now],L,m,x,y);    if(x>m) return query0(rc[now],m+1,R,x,y);    return max(query0(lc[now],L,m,x,y),query0(rc[now],m+1,R,x,y));}int query1(int now,int L,int R,int x,int y){    if(x<=L && R<=y) return maxv1[now];    int m=L+R>>1;    if(y<=m) return query1(lc[now],L,m,x,y);    if(x>m) return query1(rc[now],m+1,R,x,y);    return max(query1(lc[now],L,m,x,y),query1(rc[now],m+1,R,x,y));}void dp1(){    f[1]=1;    update(rt,0,MAX,h[1],f[1]);    for(int i=2;i<=N;i++)    {        int t0=0,t1=0;        if(h[i]!=MAX) t0=query0(rt,0,MAX,h[i]+1,MAX);        if(h[i]!=0) t1=query1(rt,0,MAX,0,h[i]-1);        f[i]=max(t0,t1)+1;        update(rt,0,MAX,h[i],f[i]);    }}void dp2(){    g[1]=1;    update(rt,0,MAX,h[1],g[1]);    for(int i=2;i<=N;i++)    {        int t0=0,t1=0;        if(h[i]!=0) t0=query0(rt,0,MAX,0,h[i]-1);        if(h[i]!=MAX) t1=query1(rt,0,MAX,h[i]+1,MAX);        g[i]=max(t0,t1)+1;        update(rt,0,MAX,h[i],g[i]);    }}void work100(){    ready();    set_segtree(rt,0,MAX);    dp1();    np=rt=0;    memset(lc,0,sizeof(lc));    memset(rc,0,sizeof(rc));    memset(maxv0,0,sizeof(maxv0));    memset(maxv1,0,sizeof(maxv1));    set_segtree(rt,0,MAX);    dp2();    int ans=0;    for(int i=1;i<=N;i++)        ans=max(ans,max(f[i],g[i]));    printf("%d\n",ans);}int main(){    freopen("test.in","r",stdin);    freopen("test.out","w",stdout);    data_in();    work100();    return 0;}

话说我还说了贪心是吧?
实际上思路还是很简单,对于一段连续上升或下降的序列,删除除去最高点和最低点以外的中间点,剩下的点的数量就是答案(实际上也就是拐点的数量,第一株花一定是留下来的,最后一株花如果不和倒数第二株花一样高也要留下来)。

AC代码(贪心):

#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<algorithm>#include<cmath>#include<queue>#include<set>#include<map>#include<cctype>#include<vector>#include<ctime>using namespace std;const int maxn=100005;int N,h[maxn];void _scanf(int &x){    x=0;    char ch=getchar();    while(ch<'0'||ch>'9') ch=getchar();    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();}void data_in(){    _scanf(N);    for(int i=1;i<=N;i++) _scanf(h[i]);}void work100(){    int ans=0;    int ok=-1;//0表示原先是下降,1表示原先是上升     for(int i=2;i<=N;i++)    {        if(h[i]==h[i-1]) continue;        if(h[i]>h[i-1])        {            if(ok!=1) ans++;            ok=1;        }        if(h[i]<h[i-1])        {            if(ok!=0) ans++;            ok=0;        }    }    if(h[N]!=h[N-1] && N!=1) ans++;    printf("%d\n",ans);}int main(){    freopen("test.in","r",stdin);    freopen("test.out","w",stdout);    data_in();    work100();    return 0;}
原创粉丝点击