区间dp

区间DP

  • 是一类子问题从短到长,而不是从左到右的动态规划问题。

状态

一般是:dpi,jdp_{i,j} 表示将区间 [i,j][i,j] 的元素合并、拆分的最值、方案数。

转移

1
2
3
4
5
6
7
for (int len = 2; len <= n; len++)//这问题
for (int i = 1; i + len - 1 <= n; i++)//状态
{
int j = i + len - 1;
for (int k = i; k < j; k++)//决策点
dp[i][j] = 运算(dp[i][j], 状态转移方程);
}

例题

洛谷P1775

P1775 石子合并(弱化版)

设有 N(N300)N(N \le 300) 堆石子排成一排,其编号为 1,2,3,,N1,2,3,\cdots,N。每堆石子有一定的质量 mi (mi1000)m_i\ (m_i \le 1000)。现在要将这 NN 堆石子合并成为一堆。每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻。合并时由于选择的顺序不同,合并的总代价也不相同。试找出一种合理的方法,使总的代价最小,并输出最小代价。

状态

dpi,jdp_{i,j} 表示将区间 [i,j][i,j] 的元素合并的最小代价。

转移

1
2
3
for (int k = i; k < j; k++)
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j]);
dp[i][j] += sum[j] - sum[i - 1];

答案

dp1,ndp_{1,n}

洛谷P3146

P3146 [USACO16OPEN] 248 G

贝西喜欢在手机上下载游戏来玩,尽管她确实觉得对于自己巨大的蹄子来说,小小的触摸屏用起来相当笨拙。

她对当前正在玩的这个游戏特别感兴趣。游戏开始时给定一个包含 NN 个正整数的序列(2N2482 \leq N \leq 248),每个数的范围在 1401 \ldots 40 之间。在一次操作中,贝西可以选择两个相邻且相等的数,将它们替换为一个比原数大 1 的数(例如,她可以将两个相邻的 7 替换为一个 8)。游戏的目标是最大化最终序列中的最大数值。请帮助贝西获得尽可能高的分数!

状态

dpi,jdp_{i,j} 表示将区间 [i,j][i,j] 的元素合并的最大能得到的数。

转移

1
2
3
for (int k = i; k < j; k++)
if (dp[i][k] == dp[k + 1][j])
dp[i][j] = max(dp[i][j], dp[i][k] + 1);

答案

max(dpi,j)max(dp_{i,j})

洛谷P1622

P1622 释放囚犯

Caima 王国中有一个奇怪的监狱,这个监狱一共有 PP 个牢房,这些牢房一字排开,第 ii 个紧挨着第 i+1i+1 个(最后一个除外)。现在正好牢房是满的。

上级下发了一个释放名单,要求每天释放名单上的一个人。这可把看守们吓得不轻,因为看守们知道,现在牢房中的 PP 个人,可以相互之间传话。如果某个人离开了,那么原来和这个人能说上话的人,都会很气愤,导致他们那天会一直大吼大叫,搞得看守很头疼。如果给这些要发火的人吃上肉,他们就会安静点。

状态

dpi,jdp_{i,j} 表示将区间 [ai,aj][a_i,a_j] 的人放在一起需要给的肉的数量。

转移

1
2
3
for (int k = i; k < j; k++)
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j]);
dp[i][j] += sum[j] - sum[i - 1] + j - i - 1;

答案

dp1,ndp_{1,n}

洛谷P2890

P2890 [USACO07OPEN] Cheapest Palindrome G

给定一个由 nn 个不同的小写字母构成的长 mm 的字符串 ss。可以通过ss 的任意位置增减字母将 ss 改为回文串。增减字母的花费不同,求最小花费。

状态

dpi,jdp_{i,j} 表示将区间 [i,j][i,j] 变成回文串的最小花费。

转移

1
2
3
if (s[i] == s[j])
dp[i][j] = (len > 2 ? dp[i + 1][j - 1] : 0);
dp[i][j] = min({ dp[i][j],dp[i][j - 1] + a[s[j]],dp[i + 1][j] + a[s[i]] });

答案

dp1,ndp_{1,n}

类似题

洛谷P2858

P2858 [USACO06FEB] Treats for the Cows G/S

约翰经常给产奶量高的奶牛发特殊津贴,于是很快奶牛们拥有了大笔不知该怎么花的钱。为此,约翰购置了 NN1N20001 \leq N \leq 2000) 份美味的零食来卖给奶牛们。每天约翰售出一份零食。当然约翰希望这些零食全部售出后能得到最大的收益,这些零食有以下这些有趣的特性:

  • 零食按照 1,,N1, \ldots, N 编号,它们被排成一列放在一个很长的盒子里。盒子的两端都有开口,约翰每天可以从盒子的任一端取出最外面的一个。
  • 与美酒与好吃的奶酪相似,这些零食储存得越久就越好吃。当然,这样约翰就可以把它们卖出更高的价钱。
  • 每份零食的初始价值不一定相同。约翰进货时,第i份零食的初始价值为 ViV_i1V10001 \leq V \leq 1000)。
  • 第i份零食如果在被买进后的第 aa 天出售,则它的售价是 Vi×aV_i \times a

ViV_i 的是从盒子顶端往下的第i份零食的初始价值。约翰告诉了你所有零食的初始价值,并希望你能帮他计算一下,在这些零食全被卖出后,他最多能得到多少钱。

状态

dpi,jdp_{i,j} 表示只将区间 [ai,aj][a_i,a_j] 买完的最大价值。

转移

1
dp[i][j] = max(dp[i][j - 1], dp[i + 1][j]) + sum[j] - sum[i - 1];

答案

dp1,ndp_{1,n}

洛谷P4170

P4170 [CQOI2007] 涂色

状态

dpi,jdp_{i,j} 表示将区间 [i,j][i,j] 达到目标的最少涂色次数。

转移

1
2
3
4
5
if (s[i] == s[j])
dp[i][j] = min(dp[i][j - 1], dp[i + 1][j]);
else
for (int k = i; k < j; k++)
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j]);

答案

dp1,ndp_{1,n}

洛谷P3205

P3205 [HNOI2010] 合唱队


小 A 心中有一个理想队形,他想知道多少种初始队形可以获得理想的队形。
请求出答案对 1965082719650827 取模的值。

状态

dpi,j,0/1dp_{i,j,0/1} 表示这一次是添加到开头/结尾,将区间 [i,j][i,j] 达到理想的队形的方案数。

转移

1
2
3
4
5
6
7
8
if (a[i] < a[i + 1])
dp[i][j][0] = (dp[i][j][0] + dp[i + 1][j][0]) % mod;
if (a[i] < a[j])
dp[i][j][0] = (dp[i][j][0] + dp[i + 1][j][1]) % mod;
if (a[j] > a[j - 1])
dp[i][j][1] = (dp[i][j][1] + dp[i][j - 1][1]) % mod;
if (a[j] > a[i])
dp[i][j][1] = (dp[i][j][1] + dp[i][j - 1][0]) % mod;

答案

(dp1,n,0+dp1,n,1)mod19650827(dp_{1,n,0}+dp_{1,n,1})\mod 19650827

总结

  1. 题目特征上有合并类、拆分类、两端处理类;
  2. 求最值类区间DP,需要注意无效状态初始化;
  3. 输出答案前,判断是否需要枚举;
  4. 决策点一般是在中间枚举或是两个端点;
  5. 若转移时可以从 dpi+1,j1dp_{i+1,j-1} 递推,需要注意 len=2len=2 的情况;
  6. 初始状态要分析清楚。