前言;
区间dp:区间动态规划(Interval DP)是一种求解最优子结构问题的动态规划(DP)方法,主要用于处理需要在区间上做决策的优化问题。它通常应用于求解那些涉及到一个区间范围(如数组或字符串的某一部分)并要求找到最优划分或最优选择的问题。
区间dp一般可以分为两种这里要注意的是问题的连续性。
·一个大问题可以分成多个连续的小问题来处理
·多个连续的小问题可合成一个大问题来处理
基本思路
·确定区间长度
·枚举每一个区间的长度
·将每一个区间分成左右两个任意的子区间
·列出状态转移方程建立dp表(dp表是dp的核心)
代码模板
for(int len=1;len<=N;i++)//枚举每一个区间长度
for(int i=1;i+len-1<N;i++) //区间右移
int j=i+len-1;//区间的右边界
for(int k=i;k<=jk++) {//将区间分成任意的左右两个子子区间
dp[i][j] = max(dp[i][j], dp[i][k] + dp[k + 1][j]);//状态转移方程建立dp表
}
return 0;
列题讲解
一:石子合并
有 n堆石子排成一排,每堆石子有一定的数量。现在我们要将 nn 堆石子并成为一堆,每次只能合并相邻的两堆石子,合并的花费为这两堆石子的总数。经过 n−1n−1 次合并后会成为一堆,求总的最小花费。
题目分析
这是一道经典的区间dp问题通过观察可以发现要求两个区间合并后花费的总代价就是这两个区间的dp数之和再加上区间合并后这个大区间的前缀和即(dpi+dpk+1+suni)这就是状态转移方程
//
// Created by ccc on 2024/12/14.
//
#include<bits/stdc++.h>
const int N=1e4;
using namespace std;
int dp[N][N];
void dpin(int n,int c)
{
for(int len=1;len<=n;len++)//枚举每一个区间长度
{
for(int i=1;i+len-1<=n;i++) //区间右移
{
int j=i+len-1;//区间的右边界
for(int k=i;k<j;k++) {//将区间分成任意的左右两个子子区间
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+c[j]-c[i-1]);//状态转移方程
}
}
}
}
int main()
{
int n;
int sum[N],c[N];//石子的前缀和
memset(dp,127,sizeof(dp));//将dp数组初始化为无穷大,因为要求最小的,不然后面取小
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>c[i];
dp[i][i]=0;
c[i]+=c[i-1];
}
dpin(n,c);
cout<<dp[1][n]<<'\n';
}
涂色问题
假设你有一条长度为 55 的木版,初始时没有涂过任何颜色。你希望把它的 55 个单位长度分别涂上红、绿、蓝、绿、红色,用一个长度为 55 的字符串表示这个目标:RGBGR
每次你可以把一段连续的木版涂成一个给定的颜色,后涂的颜色覆盖先涂的颜色。例如第一次把木版涂成 RRRRR
,第二次涂成 RGGGR
,第三次涂成 RGBGR
,达到目标。用尽量少的涂色次数达到目标。
问题分析
这类dp问题最主要的就是求出dp表 观察这个颜色区间可以把问题分为两种情况当区间两端颜色相同时dpi等于dpi-1也可以等于dpi.当两端颜色不相等时dpl=dpl+dpk+1由此可以列出他的状态方程
//
// Created by ccc on 2024/12/19.
//
#include "bits/stdc++.h"
const int N=1e3;
int dp[N][N];
using namespace std;
int main()
{
string s;
cin>>s;
memset(dp,127,sizeof(dp));//将dp数组初始化为无穷大,因为要求最小的,不然后面取
int n=s.size();// 求出所给字符串长度
for (int i=0;i<n;i++)
{
dp[i][i]=1;
}
for(int len=2;len<=n;len++)
{
for(int i=1;i+len-1<= n;i++)
{
int j=i+len-1;
if(s[i-1]==s[j-1]) dp[i][j]=min (dp[i+1][j], dp[i][j-1]);//注意因为字符串s下表是从0开始的所以将ij减一
else for(int k=i;k<j;k++)
{
dp[i][j]=min (dp[i][j],dp[i][k]+dp[k+1][j]);
}
}
}
cout<<dp[1][n];
}
总结
可以看到这类题目我都可以按照这样的分析步骤来解。所以最重要地是能看出来题型确定题型做题就很快了。