【算法模板】动态规划:区间DP
概念
区间DP也属于线性DP中的一种,它以“区间长度”作为DP 的“阶段”,使用两个坐标(区间的左、右端点)描述每个维度。在区间DP中, 一个状态由若干个比它更小且包含于它的区间所代表的状态转移而来,因此,区间D 的决策往往就是划分区间的方法。区间DP 的初态一般就由长度为1的“元区间”构成。这种向下划分再向上递推的模式与某些树形结构,也有很大的相似之处。
编程实现动态规划的状态转移方程时,务必分清阶段、状态与决策,三者应当按照从外到内的顺序依次循环
区间DP一般遵循以下方法:
- 从小到大枚举区间长度。
- 对于每一个区间长度,枚举该长度的每一个区间。
- 对于每一个区间,枚举所有可能出现的两个子区间的组合。
- 对于每一个子区间的组合,计算出合并所需代价。
- 求出当前区间的最优值。例如令f[i][j]为区间[l,r]的最大价值,则f[i][j]=max{f[i][k]+f[k+1][r]+cost|l<=k<r}.
模板
for (int x = 0; x < n; x++){//枚举长度
for (int i = 1; i + x <= n; i++){//枚举起点
dp[i][i] = 1;
int j = x + i;//终点
dp[i][j] = dp[i + 1][j] + 1;
for (int k = i + 1; k <= j; k++) {
if (a[i] == a[k])
dp[i][j] = min(dp[i][j], dp[i][k ‐ 1] + dp[k +1][j]);
}
}
}
例题
石子合并
#include <bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
int main(){
int n;cin>>n;
vector<int> arr(n+1);
vector<vector<int>> dp(n+1,vector<int>(n+1,INF));
for(int i=1;i<=n;i++)cin>>arr[i];
for(int i=1;i<=n;i++)arr[i]+=arr[i-1];
for(int i=1;i<=n;i++)dp[i][i]=0;
for(int len=2;len<=n;len++){
for(int l=1;l+len-1<=n;l++){
int r=l+len-1;
for(int mid=l;mid<r;mid++){
dp[l][r]=min(dp[l][r],dp[l][mid]+dp[mid+1][r]+arr[r]-arr[l-1]);
}
}
}
cout<<dp[1][n]<<endl;
}
涂色
#include <bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
int main(){
string S;cin>>S;
const int N=S.size();
vector<vector<int>> dp(N,vector<int>(N,INF));
for(int i=0;i<N;i++)dp[i][i]=1;
for(int len=2;len<=N;len++){
for(int l=0;l+len-1<N;l++){
int r=l+len-1;
if(S[l]==S[r]){
dp[l][r]=min(dp[l+1][r],dp[l][r-1]);
}
else for(int k=l;k<r;k++){
dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]);
}
}
}
cout<<dp[0][N-1]<<endl;
}
制作回文串
#include <bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
int main(){
int n,m;cin>>n>>m;
string s;cin>>s;
int cost[26]={0};
while(n--){
char c;cin>>c;
int x,y;cin>>x>>y;
cost[c-'a']=min(x,y);
}
vector<vector<int>> dp(m,vector<int>(m,INF));
for(int i=0;i<m;i++)dp[i][i]=0;
for(int len=2;len<=m;len++){
for(int l=0;l+len-1<m;l++){
int r=l+len-1;
if(s[l]==s[r]){
if(len==2)dp[l][r]=0;
else dp[l][r]=dp[l+1][r-1];
}
else dp[l][r]=min(dp[l][r-1]+cost[s[r]-'a'],dp[l+1][r]+cost[s[l]-'a']);
}
}
cout<<dp[0][m-1]<<endl;
}
能量项链
#include <bits/stdc++.h>
int main(){
int n;std::cin>>n;
std::vector<int> arr(n*2+1);
for(int i=0;i<n;i++)std::cin>>arr[i],arr[n+i]=arr[i];
arr[n*2]=arr.front();
std::vector<std::vector<int>> dp(n*2,std::vector<int>(n*2));
for(int len=2;len<=n;len++){
for(int l=0;l+len-1<n*2;l++){
int r=l+len-1;
for(int k=l;k<r;k++){
dp[l][r]=std::max(dp[l][r],dp[l][k]+dp[k+1][r]+arr[l]*arr[k+1]*arr[r+1]);
}
}
}
int ans=0;
for(int l=0;l<=n;l++)ans=std::max(ans,dp[l][l+n-1]);
std::cout<<ans<<std::endl;
}