DP練習題——洛谷P1970花匠

題目描述:

洛谷\(P1970\)git

花匠棟棟種了一排花,每株花都有本身的高度。花兒越長越大,也愈來愈擠。棟棟決定把這排中的一部分花移走,將剩下的留在原地,使得剩下的花能有空間長大,同時,棟棟但願剩下的花排列得比較別緻。算法

具體而言,棟棟的花的高度能夠當作一列整數\(h_1,h_2,...,h_n\)。設當一部分花被移走後,剩下的花的高度依次爲\(g_1,g_2,...,g_m\),則棟棟但願下面兩個條件中至少有一個知足:函數

條件 \(A\):對於全部\(g_{2i}>g_{2i-1},g_{2i}>g_{2i+1}\)學習

條件 \(B\):對於全部\(g_{2i}<g_{2i-1},g_{2i}<g_{2i+1}\)spa

注意上面兩個條件在\(m=1\)時同時知足,當\(m > 1\)時最多有一個能知足。code

請問,棟棟最多能將多少株花留在原地。get

輸入輸出格式:

輸入格式:

第一行包含一個整數\(n\),表示開始時花的株數。it

第二行包含\(n\)個整數,依次爲\(h_1,h_2,...,h_n\),表示每株花的高度。io

輸出格式:

一個整數\(m\),表示最多能留在原地的花的株數。

輸入輸出樣例:

輸入樣例:

5
5 3 2 1 2

輸出樣例:

3

題目分析:

​ 簡單理解一下題意:

大致就是說要求留在原地的花知足:序號爲\(2\)的倍數的花是左右兩盆花中最高或者是最矮的

解法一:

思路:

​ 既然是\(DP\)的題,那麼咱們先考慮貪心(實際上是爲了記念\(lz\)):

說句閒話:學習\(DP\)的最好方法是什麼??

貪心!!!

好了好了,說正經的。

不過仍是要吐槽這道題的算法標籤,居然沒人評貪心(也不知道\(lz\)評了沒有)

咱們能夠發現,這道題就是對一個有波動的函數進行簡化,咱們能夠舉兩個例子,一個是樣例:

咱們能夠把這個圖像看作一個具備兩個單調區間的函數圖像,咱們只能去單調區間兩端的點構成新的序列,也就是說咱們最多能保留三盆花

另外一個是從討論版裏找來的神仙數據:

有了上面的分析,咱們能夠很輕易地找出\(3\)個單調區間,也就是說最多有\(4\)盆花可以被留下

算法:

​ 根據兩組數據,咱們能夠獲得這樣一個算法:

掃描全部花的高度,從而得出增減區間的個數\(w\),輸出\(w+1\)

代碼實現:

#include<cstdio>
#include<iostream>

using namespace std;

inline int read()
{
    int F=1,num=0;
    char c=getchar();
    while(!isdigit(c)){if(c=='-') F=-1; c=getchar();}
    while(isdigit(c)){num=num*10+c-'0'; c=getchar();}
    return num*F;
}

int h[10000010];
int n;
int ans=1;//考慮到函數的左區間
int direction;

int main()
{
    n=read();
    for(int i=1;i<=n;++i)
        h[i]=read();//讀入 
    if(h[2]>=h[1])  direction=1;//1表示增區間,0表示減區間 ,由於前兩個是默認都選的,因此在這裏加上了等號
    for(int i=1;i<=n;++i)//我知道能夠邊讀入邊判斷,可是我願意,你管着嗎?? 
    {
        if(i==n)
        {
            ans++;//考慮到函數的右區間,當到了右區間時答案直接加一而且跳出循環
            break;
        }
        if(h[i+1]>h[i])
        {
            if(direction==0)//若是忽然進入到增區間,就標記爲增區間而且答案加一
            {
                ans++;
                direction=1;
            }
        }
        if(h[i+1]<h[i])
        {
            if(direction==1)//若是忽然進入到減區間,就標記爲減區間而且答案加一
            {
                ans++;
                direction=0;
            }
        }
    }
    printf("%d",ans);//輸出答案
    return 0;
}

解法二:

思路:

固然是已經哭暈在廁所的\(DP\)

\(dp[i][0]\)表示在\(i\)處上升時的最大盆數,\(dp[i][1]\)表示在\(i\)處降低時的最大盆數

直接獲得狀態轉移方程

\(if(a[i]>a[i-1])\space\space\space dp[i][0]=dp[i-1][1]+1;\\if(a[i]<a[i-1])\space\space\space dp[i][1]=dp[i-1][0]+1;\\dp[i][0]=max(dp[i][0],dp[i-1][0]);\\dp[i][1]=max(dp[i][1],dp[i-1][1]);\)

解釋一下:

\(a[i]\)是上升的時候,將上升時的最大盆數更新爲上一盆降低時的最大盆數+\(1\),而且用這兩種狀態的最大值來維護\(dp[i][0]\),表示知足條件\(B\)

\(a[i]\)是降低的時候,將降低時的最大盆數更新爲上一盆上升時的最大盆數+\(1\),而且用這兩種狀態的最小值來維護\(dp[i][1]\),表示知足條件\(A\)

​ 若是還不能理解就畫個圖,手推一下233~~

算法:

​ 就是將\(dp[1][1]\)\(dp[1][0]\)都更新爲\(1\),而後從\(2\)開始跑一遍循環,\(CV\)上面的方程就結束了

Talk is cheap,show me the code:

#include<cstdio>
#include<iostream>

using namespace std;

const int maxn=101000;
int dp[maxn][2],a[maxn];
int n;

inline int read()
{
    int F=1,num=0;
    char c=getchar();
    while(!isdigit(c)){if(c=='-')   F=-1;   c=getchar();}
    while(isdigit(c)){num=num*10+c-'0';     c=getchar();}
    return F*num;
}

int main()
{
    n=read();
    dp[1][0]=1;dp[1][1]=1;
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=2;i<=n;i++)
    {
        if(a[i]>a[i-1]) dp[i][0]=dp[i-1][1]+1;
        if(a[i]<a[i-1]) dp[i][1]=dp[i-1][0]+1;
        dp[i][0]=max(dp[i][0],dp[i-1][0]);
        dp[i][1]=max(dp[i][1],dp[i-1][1]);
    }
    printf("%d\n",max(dp[n][1],dp[n][0]));
    return 0;
}

結語:

貪心大法真的好誒(逃

相關文章
相關標籤/搜索