POJ - 3017 Cut the Sequence : 單調隊列優化dp

題目點此跳轉

思路

 題目意思是給你一個數組, 現在讓你將整個數組劃分爲幾個子區間,並且要保證每個子區間的元素和不大於M, 求 每個子區間的最大值 的和 的最小值。

 這是一道dp題, 單調隊列的一個很大的應用就是可以優化某一類dp。
 我們先將它當作一個純dp題去寫狀態轉移方程,之後再考慮使用單調隊列優化。

 設 f(i) 爲數組A[1, , i]滿足條件的每個子區間的最大值的和 的最小值, 則要求的結果即爲 f(n);
那麼狀態轉移方程爲:

f(j) = min(f(i) + max(A[k])), 0 < i < k < j;

  可以這樣理解 ,每增加一位元素,都要從這個元素出發向左擴展出一個區間,然後算這個區間裏的最值加上它前面算好的f(i), 前提是這個區間的和也不能超過M。

  如果直接枚舉的話肯定也能出答案,但是會超時, 所以單調隊列就派上用場了,我們的單調隊列裏可以保存對應區間範圍內的最值,在每次向右滑動的時候更新這些最值,然後在dp的時候取出這些最值即可。

代碼

(代碼來自tsy)

int n, num[maxn], q[maxn];
LL m, sum, f[maxn];

int main() {

    scanf("%d%lld", &n, &m);
    int pos = 0, head = 0, tail = 0;
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &num[i]);
        sum += num[i];
        if(num[i] > m) { puts("-1"); return 0; }
        while(sum > m) sum -= num[++pos];
        while(head<=tail && q[head] <= pos) ++head;
        while(head<=tail && num[q[tail]] <= num[i]) --tail;
        q[++tail] = i, f[i] = INF;
        for(int j = head, k = pos; j <= tail; k = q[j], ++j) {
            f[i] = min(f[i], f[k]+num[q[j]]);
        }
    }
    printf("%lld\n", f[n]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章