区间dp学习笔记

区间dp学习笔记

具体思路

区间dp,故名思意,就是在一段区间里做动态规划操作,一般的区间dp都运用了如下操作

  1. 状态表示:\(dp_{i,j}\) 一般表示的是 \(i\)\(j\) 这一段区间里面的答案 ,答案为 \(dp_{1,n}\)
  2. 边界处理:\(dp_{i,i}\) 只表示一个数字,我们直接按照题意初始化
  3. 三层循环,遍历区间长度 \(len\) ,左节点 \(l\) ,易得右节点 \(r\)\(len+i-1\) ,中间一些判断语句,最后一层循环在区间内寻找断点 \(k\)

一般的状态转移方程为

\(dp_{i,j} = \max(dp_{i,j},dp_{i,k}+dp_{k+1,j}+......)\)

至于你前面的 \(\max\) 还是 \(\min\),我们根据题意来判断,注意我们处理的区间还可以是环状的,这时后我们就要来处理一下边界,并将数组开大,再处理一下即可。

综上所述,一般的区间dp时间复杂度为 \(O(n^3)\) ,空间复杂度为 \(O(n^2)\)

习题

1.P1775 石子合并(弱化版)

我们设 \(f_{i,j}\)\(i\)\(j\) 的区间内的最小代价,再用前缀和优化

边界条件:\(f_{i,i}=0\)

状态转移方程

\[f_{l,r} = \min(f_{l,r}, f_{l,k} + f_{k+1,r} + s_r - s_{l-1}); \]

\(s\) 数组为前缀和数组,答案为 \(f_{1,n}\)

2.P4170 [CQOI2007]涂色

我们设 \(dp_{i,j}\)\(i\)\(j\) 的区间内的最小涂色次数

边界条件:\(dp_{i,i}=1\)

状态转移方程分成两种情况

1.\(str_l=str_r\) 的时候

可以想到只需要在首次涂色时多涂一格即可,状态转移方程:\(dp_{l,r}=\min(dp_{i+1,j},dp_{i,j-1})\)

2.否则枚举断点:

\(dp_{l,r} = min(dp_{l,r}, dp_{l,k} + dp_{k+1,r});\)

for (int x = 1; x < n; x ++ )
{
    for (int l = 1, r = x + 1; r <= n; l ++ , r ++ )
    {
        if(s[l] == s[r]) dp[l][r] = min(dp[l + 1][r], dp[l][r - 1]);
        else
            for (int k = l; k < r; k ++ ) dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]);
    }
}

3.P1063 [NOIP2006 提高组] 能量项链
\(dp_{i,j}\)\(i\)\(j\) 的区间内的最大能量

边界条件:\(dp_{i,i + 1} = 0\)

\(n\) 个珠子合并弄成 \(n + 1\) 个"石头"合并

状态转移方程:\(dp_{l,r} = max(dp_{l,r}, dp_{l,k} + dp_{k,r} + a_l * a_k * a_r);\)

典型的环形区间dp

for (int len = 2; len <= n + 1; len ++ )
{
    for(int l = 1, r; l + len - 1 <= n << 1; l ++ )
    {
        r = l + len - 1;
        if(len == 2)
            dp[l][r] = 0;
        else
        {
            for(int k = l + 1; k < r; k ++ )
                dp[l][r] = max(dp[l][r], dp[l][k] + dp[k][r] + a[l] * a[k] * a[r]);
        }
    }
}
posted @ 2022-07-31 17:26  ljfyyds  阅读(117)  评论(2)    收藏  举报