该练习 DP 了!

区间DP

洛谷P3147Problem

定义 \(f[i][j]\) 存储从左端点 \(j\) 开始,能合并出 \(i\) 的右端点位置,将其设为 \(k\)

下面我们推转移方程。从题意可以看出,两个相邻的 \(i-1\) 能够合并出 \(i\) 。那么在 \(f[i][j]\) 后所对应的就是 \(f[i][k]\),这两个 \(i\)合并能够得到一个\(i-1\),左端点是 \(j\) ,右端点是新得到的 \(f[i+1][j]\)

\[f[i][j] = f[i-1][f[i-1][j]] \]

初始化时,对于每一个 \(a[i]\) ,能够合并出其本身(实际上并不需要合并)的右端点只需要+1即可,即

\[f[a[i]][i] = i+1 \]

CF round715CProblem

对题目贪心分析可知:整个序列的最大值或者最小值一定要放在最后,那么在从前往后统计差值时,一定不会变劣(所有值一定比最大值小或者比最小值大,那么差值一定会小)。
\(a\) 数组正向排序。假定我们最后一个值已经确定(实际上,\(d_{n}\) 是确定的),对于剩下的这个数列,依然遵从上面的结论。那么我们可以推广出如下结论:对于新排出的序列(假设其为 \(b_{n}\) ),一定是由排序后的 \(a_{n}\) 组成的一系列子段所构成的。因为对于每一个点,选择有且仅有当前剩余数的最大值或者最小值。假设和上一个选的相同(均为最大值或者最小值),那么就可以构成一个子段。如果选择相反,那么就相当于多开一个子段。
(以下的 \(a_{i}\) 均为排序过后的)
那么,我们设 \(dp[i][j]\) 为前 \(j-i+1\) 个数放 \(a_{i},a_{i+1},...,a_{j}\) 时,最小的 \(\sum_{k=1}^{j-i+1} d_{k}\)
对于 \(dp[i][j]\) ,最后一个添加进来的数仅可能为 \(a_{i}\) (最小值)或者 \(a_{j}\) (最大值)。可得递推式:

\[dp[i][j] = min(dp[i+1][j],dp[i][j-1])+a[j]-a[i] \]

区间DP,\(O(n^2)\) 转移即可。

CF edu83EProblem

考虑 \(dp[l][r]\)\([l, r]\) 这段区间合并的最小长度, \(w[l][r]\) 为为 \([l, r]\) 这段区间按照 \(dp[l][r]\) 合成之后的和,枚举中间点。

\[dp[l][r] = min({dp[l][mid]+dp[mid+1][r]}) \]

\[if \ w[l][mid] \ = \ w[mid+1][r] \ : dp[l][r] \ = \ 1 \]

\[w[l][r] = w[l][mid] \ + \ 1 \]

CF round106DProblem

一道非常复杂的区间 \(dp\) 题。
首先我们要预处理出所有左括号所匹配的右括号,在此条目按下不表。
我们设 \(dp[l][r][c_{1}][c_{2}]\) 为将区间内染色完毕,且区间左端点括号颜色为 \(c_{1}\) ,区间右端点括号颜色为 \(c_{2}\) 的所有方案数。对于 \(c_{1}, \ c_{2}\) ,我们设0为不需染色,1为红色,2为蓝色。
因为题目要求一对匹配的括号要求只有一端被染色,所以对于双端染色的 \(dp\) 值自动赋0,即:

\[dp[l][r][1][1] = dp[l][r][1][2] = \ ... \ =dp[l][r][2][2] = 0 \]

首先看最小情况,即 \(r \ - \ l \ = \ 1\) ,此时无法再切分到更小区间,即

\[dp[l][r][1][0]=dp[l][r][1][0]= \ ... \ =dp[l][r][2][0] = 1 \]

转移时需分类讨论:

  1. \(r\) 即为 \(l\) 所对应的右括号,此时大致可表示为 \(( \ ... \ )\)。此时需要注意 \(dp\) 转移时相邻括号染色必须不同的限制,其余正常从 \(dp[l+1][r-1]\) 相加转移即可。
  2. \(r\)\(l\) 完全无关。此时需要将现在的区间切分成两个部分。我们设与 \(l\) 所对应的右括号为 \(right_{l}\),两个区间被切分为 \([l, \ right_{l}]\)\([right_{l}+1, \ r]\)。递归处理两个片段,根据乘法原理相乘即可。在这种情况下,需要特判 \(right_{l}\)\(right_{l}+1\) 括号颜色是否相同 (相同就不计算)。

线性DP

CF edu182CProblem

定义 \(dp[i][j]\) 为对于第 \(i\) 组是否交换(其中 \(j\) 的数只能为0/1,分别对应不交换和交换),转移方程分为两种情况:

  1. \(a[i] \geq a[i-1]\) and \(b[i] \geq b[i-1]\) (即本次不需要交换)

    \[dp[i][1] = (dp[i][1]+dp[i-1][1]), dp[i][0] = (dp[i][0]+dp[i-1][0]) \]

  2. \(a[i] \geq b[i-1]\) and \(b[i] \geq a[i-1]\) (本次交换)

    \[dp[i][1] = (dp[i][1]+dp[i-1][0]), dp[i][0] = (dp[i][0]+dp[i-1][1]) \]

AT abc404EProblem

定义 \(dp[i]\) 为将从 \(i+1\) 开始所有的石子分到当前点的最少步数。很容易想到,我们对 \(dp\) 数组设置一个极大值,并且对最后一个 \(a[i]\) 不为0的点开始往后的 \(dp\) 数组全部设为0(显然的,这些地方无法被转移到且不需要被转移到)作为预处理。

对 位置 \(i\) 倒序枚举。对每个位置 \(i\) 所对应的可转移范围 /\([max(0,i-c[i]), i-1]\) ,用 \(j\) 表示,很显然的能够想到:

\[dp[j] = min(dp[j],dp[i]+1) \]

同时,很容易忽略的一点在于:该点往前移的范围仅限于到该点前的第一个\(a[i] = 1\)的位置 \(i\) 。根据贪心思想,将当前点的所有点转移到同一个\(1\)的身上显然是更优的(转移次数变小),但是,如果在上述边界后继续转移,之后的贡献将会和该点前的第一个\(a[i] = 1\)的位置 \(i\) 两者贡献起冲突,故代码中存在:

for(int i = n-1;i > 0; i--){
    for(int j = i-1;j >= max(0ll,i-c[i]); j--){
        dp[j] = min(dp[j],dp[i]+1);
        if(a[j]) break;//这一步用来判断边界
    }
}

CF round523CProblem

定义 \(dp[i][j]\)\(a[i]\)\(b\) 中排第 \(j\) 位的序列个数。能够得到一个很显然的递推式:

\[dp[i][j] = dp[i-1][j] +(a[i] mod j = 0) \cdot dp[i-1][j-1] \]

但是在该题的数据范围内 (\(n \leq 10^5, a[i] \leq 10^6\)) ,开二维数组显然是不能被允许的。
我们将每个 \(a[i]\) 因数分解 (此处有 \(\sqrt{a[i]}\) 的复杂度),每个因数 \(j\) 显然的可以放在 \(b\) 数组的 \(j-1\) 位置之后。为了防止一个因数在某个点上同时作用两次,因数需要从大到小倒序枚举。
至此,一维 \(dp\) 数组已经呼之欲出。我们设 \(dp[i]\) 为长度为 \(i\)\(b\) 序列个数。显然有

\[dp[i] += dp[i-1] \]

对于每一个因子 \(i\)
对于 \(i = 0\) ,默认 \(dp[0]\) 为1。

AT ABC401EProblem

在做题时想到了朴素 \(dp\) 思路:
\(dp[i][j][k]\) 为前 \(i\) 个怪物,使用 \(j\) 体力和 \(k\) 魔法能否击败,但是在 \(O(nMH)\) 的复杂度下不能被接受,考虑降维。
贪心思想可以得出,在花费相同体力的情况下,花费的魔法越少越好。
那么经过一个很经典的 \(trick\) ,我们可以设 \(dp[i][j]\) 为打败前 \(i\) 个怪物,使用体力 \(j\) 时的最小花费魔力,初始化时, \(dp[0][0] = 0\)
对于上述描述,我们可以得到如下递推式:
**不使用魔法(需要\(j + a[i+1] \leq H\)):

\[dp[i][j] -> dp[i+[j+a[i+1]]] \]

使用魔法:**

\[dp[i][x] + b[i]-> dp[i+1][x] \]

背包DP

CF round890E1Problem

树形01背包
观察题意,对于每一个 \(u,v\) ,我们只需要他们的相对大小,而不需要他们具体的值。
对于一个较为简单的树,例如只有左右两个子树,那么显然可以构造出一个所有左边子树值都小于右边子树值,那么贡献就是

\[siz_{u}*siz_{v}, (siz_{u} \leq siz_{v}) \]

对于以上的这个树,我们只是粗略的区分开两颗子树的大小关系,分别遍历两颗子树时,可以确定其中的元素(因为在上述过程中,已经确定了两颗子树的大小关系),依次遍历,便可确定整棵树的取值。
对于一个点上有多颗子树,显然的我们可以把子树分成两个集合,对于每次分出的两个集合 \(u, v\) ,求上述集合的贡献可以用01背包求解。

洛谷P2340P2340 [USACO03FALL] Cow Exhibition G

Submission
留个坑以后补,在代码里已有01背包的关键点注释。

CF round214CProblem

将体积重设为 \(a_{i}-k \cdot b_{i}\) ,价值仍然为 \(a[i]\),因为会有正负所以需要一个正向偏移量,余下即为背包 \(dp\) 模板。
Submission

洛谷P13957 2023南京区域赛GP13957 [ICPC 2023 Nanjing R] 背包

将物品按体积从小到大排序,将所有值分成两部分:第一部分为背包,在前半部分选出背包能存放的最大价值。后半部分为免费部分,在剩下的物品中挑选前 \(k\) 大的部分直接相加即可,前半部分为01背包,后半部分为优先队列维护的后缀和。
Submission

期望DP

2023江苏省赛F

\(dp[i]\) 为在用过 \(i\) 个原料下的最大期望,根据题意有递推式:

\[dp[i] = max(P \cdot (dp[i-B]+2) + (1-P) \cdot (dp[i-B]+1), Q \cdot (dp[i-B+1]+1)+(1-Q) \cdot (dp[i-B]+1)) \]

特判 \(B=1\) 的情况,此时只选择其中一种是最优解。

杂项DP

CF edu183DProblem

正难则反策略,求出数列中的非逆序对即可。由于若一个区间内没有逆序对,则该区间一定严格递增,设"非逆序值" = \(n \cdot (n-1)/2 \ -\) “逆序值”,一段长度为 \(k\) 的子段显然可以增加 \(k \cdot (k-1)/2\) 的“非逆序值”。
由于 \(n, \ k\) 很小,考虑预处理出所有状态。设 \(dp_{i, \ j}\) 为长度为 \(i\) ,"非逆序值"为 \(j\)\(1, \ ... \ ,i\) 排列。枚举从 \(i\) 往后添加的递增子段长度 \(k\) ,可以得到:

\[dp_{i+k,j+k×(k−1)/2​​}=dp_{i,j}​ \]

其中,赋初值 \(dp_{0, \ 0} \ = \ 1\)
用一个 \(dfs\) 函数求当前可执行序列,具体代码如下。

void dfs(int n,int k){
    if(!n) return;
    for(int i = 1;i <= n; i++){
        int tmp = (i-1)*i/2;
        if(k>=tmp&&dp[n-i][k-tmp]){
            for(int j = 1;j <= i; j++) ans[n-i+j] = ++c;
            dfs(n-i,k-tmp);
            return;
        }
    }
}

Submisson

洛谷P10971P10971 Cookies

感性推理可以得到,贪婪度相对较大的孩子一定可以得到相对较多的饼干(会使答案更优),先将贪婪度降序排列。
\(dp_{i, \ j}\) 为前 \(i\) 个孩子共分到 \(j\) 块饼干时的怨气最小和。转移时分两种情况:

  1. \(i+1\) 个孩子获得的饼干数比第 \(i\) 个小
  2. \(i+1\) 个孩子获得的饼干数和第 \(i\) 个相同。
    基于上述给定情况,我们需要知道第 \(i\) 个孩子的饼干数,以及 \(i\) 之前与第 \(i\) 个孩子饼干数相同的孩子个数。

在此,我们需要知道其方案是如何分配的。单调不升的序列可以通过一系列 \([1, \ d]\) 的前缀 \(1\) 相加得到,例如 \([4, \ 3, \ 3, \ 2]\) 可以通过两个 \([1, \ 1, \ 1, \ 1]\) ,一个 \([1, \ 1, \ 1, \ 0]\) ,一个 \([1, \ 0, \ 0, \ 0]\) 组成。

根据上述的答案构造方法,可以枚举前缀,相当于:

\[dp_{i, \ j} = min_{0 \leq k \leq i}(dp_{k, \ j-i}) \ + \ \sum_{p=i+1}^{k}g_{p} \]

求和式可以通过预处理前缀和解决。
Submission

posted @ 2025-09-16 22:41  AboveFrost  阅读(18)  评论(1)    收藏  举报