区间dp模板题和一般性的思路
一个大的区间可以分解为小的区间。并且这两个问题本质是一样的。并且可以通过合并子问题的过程得到较大区间的结果。
若使用递推的方式解决问题,那么要先枚举区间的长度。
题目
矩阵乘法:求一系列的矩阵的乘法,按照一定的结合律,使得运算量最小。
题解
- 枚举最后一次乘法的位置,转移的复杂度为O(n).
- 合法的子区间的个数,故状态数是O(\(n^2\)),总的时间复杂度为O(\(n^3\))
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <bits/stdc++.h> using namespace std; const int maxn = 1e3+10; int dp[maxn][maxn]; memset(dp, 0x3f, sizeof(dp)); for(int i=1; i<=n; i++) dp[i][i] = 0; int dfs(int l, int r){ if(dp[l][r]!=0x3f3f3f3f) return dp[l][r]; for(int i=l ;i<r; i++){ dp[l][r] = min(dp[l][r], dfs(l, i)+dfs(i+1, r)+a[l-1]*a[i]*a[r]); } return f[l][r]; } printf("%d\n", dfs(1, n));
|
题目
括号匹配。给一个括号的序列。选择一个字序列,使匹配的括号数最大。
题解
发现模式规律:
- 空串括号匹配(初始化的时候)
- 若s括号匹配,那么(s)与[s]是括号匹配的
- 若s, t括号匹配,那么st是括号匹配的
对于区间[l, r],若\(S_l == S_r\), 则其可由[l+1, r-1]转移得到。
枚举中间点m,认为是[l, m]和[m+1, r]的组合
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <bits/stdc++.h> using namespace std; bool match(int l, int r){ char x = s[l]; char y = s[r]; return x == '('&&y == ')' || x == '['&&y == ']'; } for(int i=1; i<n; i++) dp[i][i+1] = match(i, i+1); for(int len = 2; len<n; len++){ for(int i=1, j; (j = i+len)<=n; i++ ){ dp[i][j] = dp[i+1][j-1]+match(i, j); } for(int m=i; m<j; m++){ dp[i][j] = max(dp[i][j], dp[i][m]+dp[m+1][j]); } } printf("%d\n", f[1][n]<<1);
|
石子合并问题
N个石子排成一排,每次合并的代价为两堆石子的数量之和,并且只能是相邻的两堆石子合并。(想想不相邻的石子合并是什么问题)
- f[i][j] = min{f[i][k]+f[k+1][j]}+a[i]+\(\ldots \)+a[j],在第k个位置分割
- 预处理前缀和
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <bits/stdc++.h> using namespace std; partial_sum(a+1, a+n+1, s+1); for(int len = 1; len<n; len++){ for(int i=1, j; (j=i+len)<=n; i++){ dp[i][j] = INT_MAX; for(int k=i; k<j; k++){ dp[i][j] = min(dp[i][j], dp[i][k]+dp[k+1][j]); } dp[i][j] += s[j]-s[i-1]; } } printf("%d\n", f[1][n]);
|
变形
若N个石子绕成一个环,求最小的合并的个数。
method①:枚举断开的位置.时间复杂度O(\(n^4\))
method②:在原链的基础上,后面再接一条链。最后扫描一遍所有长度为n的合并的次数的最小值。时间复杂度为O(\(n^3\));
决策单调性和四边形不等式优化
上面的石子合并问题,可以优化到O(\(n^2\)).
定义:
- 对于区间[l, r],存在一个决策点k,使得分为区间[l, k]和区间[k+1, r]后的代建最小,记w[l][r] = k
- 经过找规律,打表,可以发现w[l][r-1]\(\le \)w[l][r]\(\le\) w[l+1][r].
- 通过决策单调性,可以将时间复杂度降为O(\(n^2\))
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| partial_sum(a+1, a+n+1, s+1); for(int i=1; i<n; i++){ dp[i][i+1] = a[i] + a[i+1]; w[i][i+1] = i; } for(int len = 2; len<n; len++){ for(int i=1, j; (j=i+len)<=n; i++){ dp[i][j] = INT_MAX; for(int k = w[i][j-1]; k<=w[i+1][j]; k++){ if(dp[i][j]<=dp[i][k]+dp[k+1][j]) continue; dp[i][j] = dp[i][k] + dp[k+1][j]; w[i][j] = k; } } f[i][j] += s[j]-s[i-1]; } printf("%d\n", f[1][n]);
|
未解决的问题