1. 程式人生 > 實用技巧 >題解 P2426 【刪數】

題解 P2426 【刪數】

洛谷題目傳送門

一眼看去:區間DP

資料範圍:三重迴圈

好了不裝B了,開始說正事

這題非常明顯是區間DP。

按照慣例,先定義狀態。

分析題目,發現除了區間左端點和右端點之外,什麼也不需要加進狀態裡。因為顯而易見除了區間左右端點,沒有什麼能夠影響答案。

所以我們定義狀態\(dp[l][r]\)為區間\([l,r]\)的最大答案。

這個“操作價值”可以兩重迴圈預處理出來,所以用\(pre[l][r]\)代表刪除區間\([l,r]\)的最大價值。非常明顯的,甚至題目裡已經直接寫明白了, 其實不用預處理,現場算就行,反正是\(O(1)\)

\[pre[l][r]= \begin{cases} a[l], & \text {$l=r$} \\ |a[l]-a[r]|\times (r-l+1), & \text{$else$} \end{cases} \]

然後就是最重要的一步——狀態轉移方程。

題目裡有一個操作,就是一個區間刪除一些數,失去一些“潛在的”價值。那麼把這個過程反過來,一個區間加上一些數,得到一些“潛在的”價值。答案就是區間\([1,n]\)的最大潛在價值。

可以得到:

\[dp[l][r]=\max_{i⊆[l,r-1]} \max (dp[l][i]+pre[i+1][r],pre[l][i]+dp[i+1][r]) \]

翻譯成人話就是,一個區間的潛在價值,等於從左邊刪去一些數或者從右邊刪去一些數後再加上刪去的數的價值的較大值。

知道了狀態轉移方程,程式碼就非常好寫了。

AC Code:

#include <bits/stdc++.h> //讚美萬能頭!
using namespace std;
#define MAXN 105
int n,a[MAXN],dp[MAXN][MAXN],pre[MAXN][MAXN];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        pre[i][i]=dp[i][i]=a[i];//因為題目中的特殊約定“如果只去掉一個數,操作價值為這個數的值。”
    }
    for(int len=2;len<=n;len++){
        for(int l=1;l<=n-len+1;l++){
            int r=l+len-1;
            pre[l][r]=dp[l][r]=abs(a[l]-a[r])*(r-l+1);//預處理,因為有可能最優方案是把區間[l,r]都刪掉,所以要給dp[l][r]也賦值。
        }
    }
    for(int len=2;len<=n;len++){
        for(int l=1;l<=n-len+1;l++){
            int r=l+len-1;
            for(int i=l;i<=r-1;i++){
                dp[l][r]=max(dp[l][r],dp[l][i]+pre[i+1][r]);
                dp[l][r]=max(dp[l][r],pre[l][i]+dp[i+1][r]);//就是狀態轉移方程233
            }
        }
    }
    printf("%d\n",dp[1][n]);//輸出總的“潛在價值”。
    return 0;
}

由於LZ非常菜,有可能有自以為是的地方,所以請在評論區無情的指出qwq