区间dp定义:
区间类DP是一类在区间上进行动态规划的最优问题,一般是根据问题设出一个表示状态的dp,
可以是二维的也可以是三维的,一般情况下为二维。然后将问题划分成两个子问题,
也就是一段区间分成左右两个区间,然后将左右两个区间合并到整个区间,
或者说局部最优解合并为全局最优解,然后得解。这类DP可以用常规的for循环来写,
也可以用记忆化搜索来写,因为这种写法相当易懂,要什么值你直接去记忆化搜索就可以了。
区间dp模版:
int dfs(int st,int ed) { if(st==ed) return dp[st][ed]=0; if(dp[st][ed]!=-1) return dp[st][ed]; dp[st][ed]=0; for(int i=st;i<ed;i++) dp[st][ed]=max(dfs1(st,i)+dfs1(i+1,ed)+a[ed]-a[st-1],dp[st][ed]); return dp[st][ed]; }
分段dp定义:
一类DP跟区间类DP很相近,也是分很多区间求解最优值,但这类问题又额外增加了限制,即对区间内的操作有数量限制。
这类问题如果我们继续用区间DP的做法,效率会不高,一般情况下复杂度会多一维。
但其实,这类问题也有他们的特性,各个区间分完之后不需要合并,这也是他们区别与区间DP的地方,进而我们采用分段的做法,枚举最后一段,归类为分段DP。
分段dp模版:
for(int i=1; i<=n; i++) { dp[i][0]=sum[i]; for(int j=1; j<=min(i-1, m); j++) for(int k=1; k<i; k++) dp[i][j]=max(dp[i][j],dp[k][j-1]*(sum[i]-sum[k])); }
先不用具体想模板写的是什么,观察模板我们可以发现:
区间DP每一个dfs()的值,也就是子区间的值都作为一个返回值纵向返回给了上一级子区间进行运算,而分段DP把大区间分成小区间后,几个小区间间横向运算,没有纵向返回给上一级子区间!
例题:算式(洛谷p1338)
基本分析:这题看似我们可以用区间DP的方式,将数据分左部分,右部分,然后用加号或乘号连接起来,但此时的问题就是每部分允许有多少乘号,
这个又得枚举。所以这样的方法可做,但复杂度要变成O(n3*m2)。那么对于这类分区间时,又对区间内操作数量有限制的DP,我们可以用分段DP的方式,
用dp[i][j]表示前i个数字使用j次乘法得到的最大值。那么起转移就是 dp[i][j]=max{dp[k][j-1]sum(a[i]-a[k])} (sum(a[i]-a[k])表示a[k+1]至a[i]的求和),从而,我们可以用O(n^2m)复杂度解决该问题。
本质就是k决定了必须分成多少段,因此只要枚举某一区间内的乘号个数(即分段数)。
#include<cstdio> #include<algorithm> using namespace std; int main() { int sum[105] = {}, n, k, dp[105][105] = {}, g; scanf("%d%d", &n, &k); for(int i = 1;i <= n;i++) { scanf("%d", &g); if(i == 1) sum[1] = g; sum[i] = g + sum[i - 1]; } for(int i = 1;i <= n;i++) { dp[i][0] = sum[i]; for(int j = 1;j <= min(i - 1,k);j++) { for(int k = 1;k <= i;k++) { dp[i][j] = max(dp[i][j],dp[k][j - 1] * (sum[i] - sum[k])); } } } printf("%d", dp[n][k]); return 0; }
原文:https://www.cnblogs.com/mint-hexagram/p/14810364.html