区间dp共有两种实现方法,一种是迭代式,另一种是记忆化搜索式。
- 一般迭代式的写法较为固定
区间dp首先从小到大枚举区间长度l。
然后枚举区间的起始点i。
然后枚举区间的分割点d。 - 记忆化搜索
acwing 282 石子合并/《算法竞赛进阶指南》
使用前缀和简化计算
#include<bits/stdc++.h>
using namespace std;
#define N 310
int n;
int a[N];
int dp[N][N];//两个维度分别是首尾指针
int pre[N];
int main(){
cin>>n;
for( int i=1;i<=n;i++){
cin>>a[i];
pre[i]=pre[i-1]+a[i];
}
memset(dp,0x3f,sizeof(dp));
for( int i=0;i<=n;i++){
dp[i][i]=0;
}
for( int l=0;l<=n;l++){
for( int i=1;i+l<=n;i++){
for( int d=i+1;d<=l+i;d++){
dp[i][i+l]=min(dp[i][i+l],dp[i][d-1]+dp[d][i+l]+pre[i+l]-pre[i-1]);
}
}
}
cout<<dp[1][n];
}
acwing 1068《信息学奥赛一本通》环形石子合并
把环形拉直成一个长度为2n的直线
这是应对环形问题常用的手段
#include<bits/stdc++.h>
using namespace std;
#define N 410
typedef long long ll;
int a[N];
int dp1[N][N];//min
int dp2[N][N];//max
int pre[N];
int main(){
int n;
cin>>n;
for( int i=1 ;i<=n ;i++) cin>>a[i];
for( int i=n+1;i<=2*n;i++) a[i]=a[i-n];
for( int i=1 ;i<=n*2;i++) pre[i]=pre[i-1]+a[i];
memset(dp1,0x3f,sizeof(dp1));
memset(dp2,0,sizeof(dp2));
for( int i=0 ;i<=2*n;i++) {
dp1[i][i]=0;
}
for( int l=1;l<=n;l++)//the lenth
for( int i=1;i+l<=n*2;i++){ //the beginner
for( int d=i+1;d<=i+l;d++){//the divsion
dp1[i][i+l]=min(dp1[i][i+l] , dp1[i][d-1]+dp1[d][i+l]+pre[i+l]-pre[i-1]);
dp2[i][i+l]=max(dp2[i][i+l] , dp2[i][d-1]+dp2[d][i+l]+pre[i+l]-pre[i-1]);
}
}
// cout<<dp1[4][6]<<endl;
int ans=0x7fffffff;
for( int i=1;i<=n;i++){
ans=min(ans,dp1[i][i+n-1]);
}
cout<<ans<<endl;
ans=0;
for( int i=1;i<=n;i++){
ans=max(ans,dp2[i][i+n-1]);
}
cout<<ans<<endl;
}
acwing320 能量项链《算法竞赛进阶指南》
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 210
ll a[N];
ll dp[N][N];
int main(){
int n;
cin>>n;
for( int i=1;i<=n;i++){
cin>>a[i];
a[i+n]=a[i];
}
ll ans=0;
a[n+n+1]=a[1];
for( int l=1;l<=n;l++)
for( int i=1;i+l<=n*2;i++){
int r=i+l-1;
for( int d=i+1;d<=r;d++){//注意控制d的范围,边界敏感
dp[i][r]=max( dp[i][r] , dp[i][d-1]+dp[d][r]+a[i]*a[d]*a[r+1] );
}
}
for( int i=1;i<=n;i++){
ans=max(dp[i][i+n-1],ans);
// cout<<i<<" "<<i+n-1<<" "<<dp[i][i+n-1]<<endl;
}
cout<<ans<<endl;
}