1. 程式人生 > >『陣列的最大代價 貪心優化DP』

『陣列的最大代價 貪心優化DP』

<更新提示>

<第一次更新>


<正文>

陣列的最大代價(51nod 1270)

Description

陣列A包含N個元素A1, A2......AN。陣列B包含N個元素B1, B2......BN。並且陣列A中的每一個元素Ai,都滿足1 <= Ai <= Bi。陣列A的代價定義如下:
\[S=\sum_{i=2}^{N}|A_i-A_{i-1}|\]
(公式表示所有兩個相鄰元素的差的絕對值之和)
給出陣列B,計算可能的最大代價S。

Input Format

第1行:1個數N,表示陣列的長度(1 <= N <= 50000)。
第2 - N+1行:每行1個數,對應陣列元素Bi(1 <= Bi <= 10000)。

Output Format

輸出最大代價S。

Sample Input

5
10
1
10
1
10

Sample Output

36

解析

化簡題目大意可以得知:即求一個數組限制下的相鄰兩項最大差值和。
有一個很簡單的暴力DP思路如下:

\(f[i][j]\)代表前\(i\)項當中,第\(i\)個數字取\(j\)的最大和。
\(f[i][j]=max\{f[i-1][k]+|j-k|\}\)
\((i=1 - n,j=1 - B_i,k=1 - B_{i-1})\)
時間複雜度\(O(nMax\{b_i\}^2)\)

不怕暴力TLE,就怕暴力不敢想。沒錯,最簡單的暴力就是這道題的關鍵。
我們可以用貪心對這個暴力進行降維打擊

優化。

最顯然的貪心,構造\(A_i\)時極端化能得到全域性最優解。即:\(A_i\)的最優取值方案只有兩種可能,\(1\)\(B_i\),這樣可以讓相鄰兩項的差的絕對值儘可能大。

那麼\(j\)\(k\)都只能取最大值或\(1\),直接實現\(O(1)\)轉移。可以優化一下狀態:設\(f[i][0/1]\)代表前\(i\)項當中,第\(i\)個數字取\(1\)(第二維為0)或\(B_i\)(第二維為1)的最大和。
狀態轉移方程:
\[f[i][0]=max(f[i-1][0],f[i-1][1]+|B_{i-1}-1|)\\f[i][1]=max(f[i-1][0]+|B_i-1|,f[i-1][1]+|B_i-B_{i-1}|)\]

可以滾動陣列一下把第一維的空間也優化了。

\(Code:\)

#include<bits/stdc++.h>
using namespace std;
inline void read(int &k)
{
    int w=0,x=0;char ch;
    while(!isdigit(ch))w|=ch=='-',ch=getchar();
    while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    k=(w?-x:x);return;
}
const int N=50000+80;
int n,b[N],f[2][2];
inline void input(void)
{
    read(n);
    for(int i=1;i<=n;i++)read(b[i]);
}
inline void dp(void)
{
    f[1][0]=f[1][1]=0;
    for(int i=2;i<=n;i++)
    {
        f[i&1][0]=max(f[i-1&1][0],f[i-1&1][1]+abs(b[i-1]-1));
        f[i&1][1]=max(f[i-1&1][0]+abs(b[i]-1),f[i-1&1][1]+abs(b[i]-b[i-1]));
    }
}
int main(void)
{
    input();
    dp();
    printf("%d\n",max(f[n&1][0],f[n&1][1])); 
} 

考點:演算法思想的結合運用。


<後記>