区间DP学习笔记

区间DP

区间DP学习笔记

时间:2023-9-24

记录人:fengqiao17


P1063 [NOIP2006 提高组] 能量项链

状态: d p i , j dp_{i,j} dpi,j 表示将区间 i i i j j j 之间的珠子合并得到的最大能量

答案: max ⁡ ( d p i , i + n − 1 ) \max(dp_{i,i+n-1}) max(dpi,i+n1)

状态转移:
d p i , j ← max ⁡ ( d p i , k , d p i , k + d p k + 1 , j + h e a d i × h e a d k + 1 × t a i l j ) dp_{i,j} \leftarrow \max(dp_{i,k},dp_{i,k}+dp_{k+1,j}+head_i \times head_{k+1} \times tail_j) dpi,jmax(dpi,k,dpi,k+dpk+1,j+headi×headk+1×tailj)

答案: max ⁡ ( d p i , i + n − 1 ) \max(dp_{i,i+n-1}) max(dpi,i+n1)

#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int head[N], tail[N];
int dp[N][N];
int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> head[i];
        if (i > 1) {
            tail[i - 1] = head[i];//头尾的标号相同
        }
    }
    tail[n] = head[1];//容易漏掉,记得加上这一行
    for (int i = 1; i <= n; i++) {
        head[i + n] = head[i];
        tail[i + n] = tail[i];
    }
    for (int len = 2; len <= n; len++) {
        for (int i = 1; i + len - 1 <= n * 2 - 1; i++) {
            int j = i + len - 1;
            for (int k = i; k < j; k++) {
                dp[i][j] = max(dp[i][j], dp[i][k] + dp[k + 1][j] + head[i] * head[k + 1] * tail[j]);//区间dp经典思路
            }
        }
    }
    int maxi = 0;
    for (int i = 1; i <= n; i++) {
        maxi = max(maxi, dp[i][i + n - 1]);//记得答案要取max
    }
    cout << maxi;
    return 0;
}

P2890 [USACO07OPEN] Cheapest Palindrome G

状态: d p i , j dp_{i,j} dpi,j 表示将 i i i j j j 的区间变为一个回文串的最小代价。

答案: d p 1 , m dp_{1,m} dp1,m

状态转移:
d p i , j = min ⁡ ( d p i , j − 1 + w s j , d p i + 1 , j + w s i ) dp_{i,j}=\min(dp_{i,j-1}+w_{s_j},dp_{i+1,j}+w_{s_i}) dpi,j=min(dpi,j1+wsj,dpi+1,j+wsi)

需要注意的是,当 s i s_i si s j s_j sj 相等时,还需要转移:
d p i , j = min ⁡ ( d p i , j , d p i + 1 , j − 1 ) dp_{i,j}=\min(dp_{i,j},dp_{i+1,j-1}) dpi,j=min(dpi,j,dpi+1,j1)

#include <bits/stdc++.h>
using namespace std;
const int N = 5e3 + 5;
int w[N];
int dp[N][N];
int main() {
    memset(dp, 0x3f, sizeof(dp));
    int n, m;
    string s;
    cin >> n >> m >> s;
    s = '*' + s;
    for (int i = 1; i <= n; i++) {
        char c;
        int a, b;
        cin >> c >> a >> b;
        w[c] = min(a, b);
    }
    for (int i = 1; i <= m; i++) {
        dp[i][i] = 0;
    }
    for (int len = 2; len <= m; len++) {
        for (int i = 1; i + len - 1 <= m; i++) {
            int j = i + len - 1;
            if (s[i] == s[j]) {
                if (len == 2) {
                    dp[i][j] = 0;
                } else {
                    dp[i][j] = dp[i + 1][j - 1];
                }
            }
            dp[i][j] = min(dp[i][j], min(dp[i][j - 1] + w[s[j]], dp[i + 1][j] + w[s[i]]));
        }
    }
    cout << dp[1][m];
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值