参考链接:https://blog.csdn.net/my_sunshine26/article/details/77141398
一、算法梗概
区间DP是一种在一个线性的空间上求得最优解的规划过程,某些问题是非线性的但仍可以转化为线性求解(例如:一个环形的数据可以将其数据量*2后首尾连接在一起求解);
二、具体思路
比较常见的思路是一种O(n3)级别的求解过程具体思路如下图代码所示:
void init()//初始化
void DP():算法实现模板
第一种为较流行的模板,理解难度大;
第二种为自己实现的模板,比较便于理解(弱鸡的自我救赎)
void init(){
dp[i][j]=Max+sum(i,j);
}
void DP(){
for(int len=2;len<=n;len++){
for(int i=1,j=i+len-1;j<=n;i++,j++){//i:start j:end
dp[i][j]=-INF;
for(int k=i;k<j;k++){//k:i...j
dp[i][j]=max(Max,dp[i][k]+dp[k+1][j]+sum(i,j));
}
}
}
}
void DP(){
for(int len=2;len<=n;len++){
for(int i=1,j=i+len-1;j<=n;i++,j++){//i:start j:end
int Max=-INF;
for(int k=i;k<j;k++){//k:i...j
Max=max(Max,dp[i][k]+dp[k+1][j]);
}
dp[i][j]=Max+sum(i,j);
}
}
}
1、模板解释
一层for:控制遍历的区间长度,因为len=1时已经初始化,所以从len=2遍历到len=n;
二层for:定义i,j分别为区间长度为len下的开头下标与结尾下标;
三层for:定义k,表示的是当前遍历的区间的划分点
例如:当i=1,j=4时,k to:1...3三个划分点分别将区间[1,4]划分为
[1,1] + [2,4]
[1,2] + [3,4]
[1,3] + [4,4]
三个区间
遍历后即可获得最优解
2、模板注意事项(第二种DP模板)
1.在二层for中对每个小区间dp前,应该对大区间dp[i][j]进行初始化,若不初始化,默认值为0可能造成无法更新dp[i][j]
2.因为sum(i,j)在dp时每个小区间是都会+的,所以不妨单独提出来最后加,更便于理解而已
三、相关例题
1、洛谷 P1880 [NOI1995]石子合并
链接:https://www.luogu.org/problemnew/show/P1880
AC代码:
#include<iostream>
#define MAX_N 205
#define INF 0x3f3f3f3f
using namespace std;
int n;
int Data[MAX_N];
int dpmin[MAX_N][MAX_N];
int dpmax[MAX_N][MAX_N];
int Sum[MAX_N];
int sum(int x,int y) {
return Sum[y]-Sum[x-1];
}
void input() {
cin>>n;
for(int i=1; i<=n*2; i++) {
if(i<=n)
cin>>Data[i];
Data[i+n]=Data[i];
Sum[i]=Sum[i-1]+Data[i];
}
n*=2;
}
void init() {
for(int i=1; i<=n; i++) {
dpmin[i][i]=0;
dpmax[i][i]=0;
}
}
void Dp() {
for(int len=2; len<=n/2; len++) {
for(int i=1,j=i+len-1; j<=n; i++,j++) { //i:start j:end
int Min=INF;
int Max=-INF;
for(int k=i; k<j; k++) { //k:i...j
Min=min(Min,dpmin[i][k]+dpmin[k+1][j]);
Max=max(Max,dpmax[i][k]+dpmax[k+1][j]);
// cout<<sum(i,j)<<endl;
}
dpmin[i][j]=Min+sum(i,j);
dpmax[i][j]=Max+sum(i,j);
}
}
}
void output() {
int ans1=INF,ans2=-INF;
for(int i=1,j=n/2 ; j<=n ; i++,j++ ) {
ans1=min(ans1,dpmin[i][j]);
ans2=max(ans2,dpmax[i][j]);
}
cout<<ans1<<endl<<ans2<<endl;
// cout<<endl;
// for(int i=1;i<=n;i++){
// for(int j=1;j<=n;j++){
// cout<<dpmin[i][j]<<" ";
// }cout<<endl;
// }cout<<endl;
// for(int i=1;i<=n;i++){
// for(int j=1;j<=n;j++){
// cout<<dpmax[i][j]<<" ";
// }cout<<endl;
// }
}
int main() {
input();
init();
Dp();
output();
return 0;
}