区间dp就是在区间上的动态规划,求解一段区间上的最优解,通过合并小区间的最优解来得到整个大区间上的最优解的算法。 区间dp一般都是三层for循环 需要注意的是 区间是从小到大 因为dp是后一个用到前一个的给出的结果 并进行递推 区间dp常用的一个状态就是dp[i][j]表示i~j这个区间的最优解是多少 区间dp的大致思路: 1.确定状态 初始化长度为 n 的dp[][]的值 2.枚举区间长度 枚举区间的起始点 (有些题还需枚举断点) 由小区间转移到大区间 3.最终答案基本是 dp[1][n] or dp[0][n-1]
区间dp 模板
1 //memset(dp,0,sizeof(dp)) 初始化DP数组 2 for(int i=1;i<=n;i++){ 3 dp[i][i]=初始值 4 } 5 for(int len=2;len<=n;len++) //区间长度 6 for(int i=1;i<=n;i++){ //枚举区间起点 7 int j=i+len-1; //区间终点 8 if(j>n) break; //越界结束 9 for(int k=i;k<j;k++){ //枚举分割点,构造状态转移方程 10 dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+w[i][j]); 11 } 12 }
升级
1.由直线型变换成环形
若是环形,则需要将整个数组复制一遍,然后在统计答案的时候,把1-n开头长度为n的区间求一个min 即可
2.优化 —— 四边形不等式优化
https://blog.csdn.net/noiau/article/details/72514812 //大佬blog 侵删
题目
1.石子合并
2.Pangu and stone
1 #include<iostream> 2 #include<cstring> 3 4 using namespace std; 5 const int inf=0x3f3f3f3f; 6 int n,l,r,a[105],sum[105],dp[105][105][105]; 7 8 void f(){ 9 for(int d=1;d<=n;d++){ //区间长度 10 for(int i=1;i+d-1<=n;i++){ //区间起点 11 int j=i+d-1; //区间终点 12 for(int k=i;k<j;k++){ //在区间内枚举 13 for(int s=l;s<=r;s++){ //一次 可 合并 区间 数 14 dp[i][j][1]=min(dp[i][j][1],dp[i][k][s-1]+dp[k+1][j][1]+sum[j]-sum[i-1]); 15 } 16 } 17 for(int k=2;k<=n;k++){ 18 for(int s=i;s<j;s++){ //枚举分割点 构造状态转移方程 19 dp[i][j][k]=min(dp[i][j][k],dp[i][s][k-1]+dp[s+1][j][1]); 20 } 21 } 22 } 23 } 24 if(dp[1][n][1]==inf)cout<<0<<endl; 25 else cout<<dp[1][n][1]<<endl; 26 } 27 28 int main(){ 29 while(cin>>n>>l>>r){ 30 for(int i=1;i<=n;i++){ 31 cin>>a[i]; 32 sum[i]=sum[i-1]+a[i]; 33 } 34 memset(dp,inf,sizeof(dp)); 35 for(int i=1;i<=n;i++){ 36 for(int j=i;j<=n;j++) 37 dp[i][j][j-i+1]=0; 38 } 39 f(); 40 } 41 return 0; 42 }