洛谷\(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\)上面的方程就結束了
#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; }
貪心大法真的好誒(逃