動態規劃【7】之區間動態規劃

區間類動態規劃是線性動態規劃的擴展,它在分階段地劃分問題時,與階段中元素出現的順序和由前一階段的哪些元素合併而來由很大的關係。令狀態f[i,j]f[i,j]表示將下標位置iijj的所有元素合併能獲得的價值的最大值,那麼f[i,j]=max{f[i,k]+f[k+1,j]+cost}f[i,j]=\max\{f[i,k]+f[k+1,j]+cost\},其中costcost爲將這兩組元素合併起來的代價1,ik<ji\leq k<j

例題一 石子合併

例題:luogu1880 [NOI1995]石子合併
思路參考自:https://oi-wiki.org/dp/interval/。簡單來說,三重for循環,第一重是長度lenlen,第二重是開始下標ii(前兩重可以確定結束下標jj),第三重是合併位置kk

解決環的問題:複製一份,變成2n2n的序列。

注意點:最小值初始化f1[i][i]=0f1[i][i]=0表示一個石頭不需要代價,其他點f1[i][j]=INFf1[i][j]=INF(無窮大)。

hyy提供的代碼:

#include <bits/stdc++.h>
using namespace std;

int f[210][210];
int f1[210][210];
int n;
int a[210];
int sum[210];

int main(){
    cin >> n;
    for(int i=1;i<=n;i++){
        cin >> a[i];
        a[i+n]=a[i];
    }
    for(int i=1;i<=2*n;i++)     sum[i]=sum[i-1]+a[i];
    for(int i=1;i<=2*n;i++){
        for(int j=1;j<=2*n;j++){
            f1[i][j]=2e6;
        }
    }
    for(int i=1;i<=2*n;i++) f1[i][i]=0,f[i][i]=0;
    for(int len=2;len<=n;len++){
        for(int i=1;i<=2*n;i++){
            int j=i+len-1;
            if(j>=2*n)   break;
            for(int k=i;k<j;k++){
                f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]);
                f1[i][j]=min(f1[i][j],f1[i][k]+f1[k+1][j]+sum[j]-sum[i-1]);
            }
        }
    }
    int ans=0,ans1=2e6;
    for(int i=1;i<=n;i++){
        ans1=min(ans1,f1[i][i+n-1]);
        ans=max(ans,f[i][i+n-1]);
    }
    cout << ans1<<endl<<ans;
    return 0;
}

例題二 能量項鍊

例題:luogu1063 能量項鍊

處理方式與例題一非常類似,唯一要修改的地方是costcostmax{f[i,k]+f[k+1][j]+cost}\max\{f[i,k]+f[k+1][j]+cost\}的cost爲a[i]a[k+1]a[j+1]a[i]*a[k+1]*a[j+1].

代碼:

/* ***********************************************
Author        : VFVrPQ
Created Time  : 一  3/ 2 14:45:08 2020
File Name     : luogu1063能量項鍊.cpp
Problem       : 
Description   : 
Solution      : 
Tag           : 
************************************************ */

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <iomanip>
using namespace std;
#define DEBUG(x) cout<<x<<endl;
const int N = 2e2+10;
const int M = 1e9+7;
const int INF = 1e9+7;

int dp[N][N];
int a[N];
int n;
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        a[n+i] = a[i];
    }
    for (int i=1;i<=n+n;i++) dp[i][i] = 0;

    for (int len=2;len<=n;len++){
        for (int i=1;i<=n+n;i++){
            int j = i+len-1;
            if (j>=n+n) break;
            for (int k=i;k<j;k++){
                dp[i][j] = max(dp[i][j], dp[i][k]+dp[k+1][j]+a[i]*a[k+1]*a[j+1]);
            }
        }
    }
    
    int ans = 0;
    for (int i=1;i<=n;i++) ans = max(ans, dp[i][i+n-1]);
    printf("%d\n",ans);
    return 0;
}

  1. https://oi-wiki.org/dp/interval/ ↩︎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章