区间DP的引入
区间DP是一种特殊的DP,与普通的DP在阶段的划分上往往有所不同。
先来看个栗子:
这道题大家都做过,通过这一道题可以体会到区间DP的特殊:
他是按照子区间的长度划分阶段,不是按找起点终点等划分。
在很多DP题中,一个区间的答案往往取决于他的子区间的答案,于是区间DP就诞生了。
这道题的分析:(做过的巨佬请绕行)
贪心显然有问题,不采用。
f i , j f_{i,j} fi,j 表示区间 i , j i, j i,j 内能取到的最大值(在此不讨论最小值)。
f i , i = a i f_{i,i}=a_i fi,i=ai
显然,我们要么先将 i + 1 ∼ j i+1\sim j i+1∼j 的区间合并,反之合并 i ∼ j − 1 i\sim j-1 i∼j−1 的区间,再加上区间 i ∼ j i\sim j i∼j的总和(可以使用前缀和数组计算)。
方程: f i , j = m a x ( f i + 1 , j , f i , j − 1 ) + ∑ i = i j a f_{i,j}=max(f_{i+1,j},f_{i,j-1})+\sum^j_{i=i} a fi,j=max(fi+1,j,fi,j−1)+∑i=ija
阶段(区间DP重点):
无论按照起点还是终点划分阶段,均不能保证一个状态的子状态一定在它之前计算出来。
观察发现,一个区间的最大值依赖于他的子区间的最大值,那么我们可以按照长度划分阶段。
外层循环 i i i 枚举序列长度,内层循环 j j j 枚举序列起点。
for (int i = 2; i <= n; i ++)
for (int j = 1; j <= n; j ++)
{
int L = (j + i - 1) % n;//注意此题含环,显然用链表形式可以解决
if (L == 0) L = n;
for (int k = j; k != L; k = r[k])
d[j][L] = min(d[j][L], d[j][k] + d[r[k]][L] + s[j][L]);
}
完整AC代码:
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int d[105][105], a[105], s[105][105], l[105], r[105];
int main()
{
memset(d, 0x3f, sizeof(d)