区间DP虽然入门十分简单,但是也需要记住一些步骤
方程形式上一般是
f(l,r)=max(f(l,k)+f(k+1,r))+d(l,r)
f
(
l
,
r
)
=
m
a
x
(
f
(
l
,
k
)
+
f
(
k
+
1
,
r
)
)
+
d
(
l
,
r
)
(k属于l到r-1)
转移有这几步
枚举区间长度
枚举左端点,结合区间长度确认右端点
枚举区间分割点,转移
还会经常遇到环形区间DP,解决方法是破环成链,复制一倍数据,加到原有数据后面
然后转移只需要把枚举左端点的上限,由
l+length≤n
l
+
l
e
n
g
t
h
≤
n
变为
l+length≤2n
l
+
l
e
n
g
t
h
≤
2
n
枚举区间长度的上限不要变,仍然是n
最后遍历一遍从第i个数(i
≤
≤
n)开始的长度为n的区间,取最大或最小值
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 1000 + 10;
int n,a[MAXN],f[MAXN][MAXN],g[MAXN][MAXN],s[MAXN];
int main() {
cin >> n;
memset(f,0x3f,sizeof(f));
for(int i=1; i<=2*n; i++) {
if(i<=n)
cin >> a[i];
if(i>n) a[i] = a[i-n];
f[i][i] = 0;
g[i][i] = 0;
s[i] = s[i-1] + a[i];
}
for(int i=2; i<=n; i++) {
for(int l=1; l+i-1<=2*n; l++) {
int r = l+i-1;
for(int k=l; k<r; k++) {
f[l][r] = min(f[l][r], f[l][k] + f[k+1][r]);
g[l][r] = max(g[l][r], g[l][k] + g[k+1][r]);
}
f[l][r] += s[r] - s[l-1];
g[l][r] += s[r] - s[l-1];
}
}
int minn = 1<<30;
int maxx = 0;
for(int i=1; i<=n; i++) {
minn = min(minn, f[i][i+n-1]);
maxx = max(maxx, g[i][i+n-1]);
}
printf("%d\n%d", minn, maxx);
return 0;
}