区间dp,Ranges Style

区间类动态规划是线性动态规划的扩展,它在分阶段地划分问题时,与阶段中元素出现的顺序和由前一阶段的哪些元素合并而来有很大的关系。

令状态f[i, j] 表示将下标位置ij 的所有元素合并能获得的价值的最大值,那么

  1. f[i, j] = max{f(i, k) + f(k+1, j) + cost}
  1. // 区间dp的几种模型特征
  2. subsets 0110101
  3. 0404500
  4. consecutive ranges
  5. (ab)(cde)(fg)
  6. nested ranges
  7. (((a)(bc))(def))(ghijkl)

Consecutive Ranges Style

例题,Pearls2a846e1562af749877f30ee62b0463f6 (1).jpg

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N = 110, INF = 0x3f3f3f3f;
  4. int a[N], p[N], n, T;
  5. int mem[N][N];
  6. int best(int u, int last)
  7. {
  8. if (u == n + 1) return 0;
  9. int &ret = mem[u][last];
  10. if (ret != INF) return ret;
  11. int sum = 10;
  12. for (int i = last; i <= u; i++) sum += a[i];
  13. //buy now, add to right now interval
  14. ret = min(ret, sum * p[u] + best(u + 1, u + 1));
  15. if (u != n) ret = min(ret, best(u + 1, last));
  16. return ret;
  17. }
  18. int main()
  19. {
  20. cin >> T;
  21. while (T--){
  22. cin >> n;
  23. for (int i = 1; i <= n; i++) cin >> a[i] >> p[i];
  24. memset(mem, 0x3f, sizeof mem);
  25. printf("%d\n", best(1, 1));
  26. }
  27. return 0;
  28. }
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N = 110, INF = 0x3f3f3f3f;
  4. int a[N], p[N], n, T;
  5. int mem[N][N];
  6. int s[N];
  7. int best(int u, int last)
  8. {
  9. if (u == n + 1) return 0;
  10. int &ret = mem[u][last];
  11. if (ret != INF) return ret;
  12. int sum = 10 + s[u] - s[last - 1];
  13. //buy now, add to right now interval
  14. ret = min(ret, sum * p[u] + best(u + 1, u + 1));
  15. if (u != n) ret = min(ret, best(u + 1, last));
  16. return ret;
  17. }
  18. int main()
  19. {
  20. cin >> T;
  21. while (T--){
  22. cin >> n;
  23. for (int i = 1; i <= n; i++) cin >> a[i] >> p[i];
  24. memset(s, 0, sizeof s);
  25. for (int i = 1; i <= n; i++) s[i] += s[i - 1] + a[i];
  26. memset(mem, 0x3f, sizeof mem);
  27. printf("%d\n", best(1, 1));
  28. }
  29. return 0;
  30. }
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N = 110, INF = 0x3f3f3f3f;
  4. int a[N], p[N], n, T, s[N];
  5. int dp[N];
  6. int main()
  7. {
  8. cin >> T;
  9. while (T--){
  10. memset(s, 0, sizeof s);
  11. memset(dp, 0x3f, sizeof dp);
  12. cin >> n;
  13. for (int i = 1; i <= n; i++) cin >> a[i] >> p[i];
  14. for (int i = 1; i <= n; i++) s[i] += s[i - 1] + a[i];
  15. dp[0] = 0;
  16. for (int i = 1; i <= n; i++)
  17. for (int j = 1; j <= i; j++)
  18. dp[i] = min(dp[i], dp[j - 1] + (s[i] - s[j - 1] + 10) * p[i]);
  19. cout << dp[n] << '\n';
  20. }
  21. return 0;
  22. }

Nested Ranges Style

例题,CasketOfStar

image.png
image.png
image.png

  1. // my_code, cannot submit to Topcoder
  2. #include <bits/stdc++.h>
  3. using namespace std;
  4. const int N = 55;
  5. int weight[N], mem[N][N];
  6. int n;
  7. int best(int i, int j)
  8. {
  9. if (j - i + 1 <= 2) return 0;
  10. if (j - i + 1 == 3) return weight[i] * weight[j];
  11. int &ret = mem[i][j];
  12. if (ret != -1) return ret;
  13. for (int k = i + 1; k < j; k++)
  14. ret = max(ret, best(i, k) + best(k, j) + weight[i] * weight[j]);
  15. return ret;
  16. }
  17. int main()
  18. {
  19. cin >> n;
  20. for (int i = 1; i <= n; i++) cin >> weight[i];
  21. memset(mem, -1, sizeof mem);
  22. cout << best(1, n) << '\n';
  23. return 0;
  24. }

例题,Cutting Sticks

image.png

  1. //Cutting Sticks,请上机练习

例题,Optimal Array Multiplication Sequence

image.png

  1. // 难度:提高+/省选-
  2. // 需要递归输出方案
  3. // 请上机操作

General Ranges Style

例题,String to Palindrome

image.png

  1. // 递归写法,my 10739_1.cpp
  2. # include<vector>
  3. # include<string>
  4. # include<iostream>
  5. using namespace std;
  6. string A, B;
  7. const int MAX = 1000+9;
  8. int memo[MAX][MAX];
  9. int best(int i, int j)
  10. {
  11. if(i >= j)
  12. return 0;
  13. int &ret = memo[i][j];
  14. if(ret != -1)
  15. return ret;
  16. ret = best(i+1, j-1) + (A[i] != A[j]);
  17. ret = min(ret, best(i+1, j)+1);
  18. ret = min(ret, best(i, j-1)+1);
  19. return ret;
  20. }
  21. int main() {
  22. int num;
  23. cin >> num;
  24. for (int i = 1; i <= num; i++) {
  25. cin >> A;
  26. memset(memo, -1, sizeof memo);
  27. cout << "Case " << i << ": " << best(0, (int)A.size()-1) << endl;
  28. }
  29. return 0;
  30. }
  1. // 循环写法
  2. #include<iostream>
  3. #include<string>
  4. using namespace std;
  5. int min(int a, int b, int c)
  6. {
  7. if(b < a) a = b;
  8. if(c < a) a = c;
  9. return a;
  10. }
  11. int A[1001][1001]; /* The cost of substring A[i][j] */
  12. int main()
  13. {
  14. int i, j, k, len, cases, diag;
  15. string str;
  16. cin>>cases;
  17. for (k = 1; k <= cases; k++)
  18. {
  19. cin>>str;
  20. for (i=0; i<str.size(); i++) /* Base case in diagonal */
  21. A[i][i] = 0; /* Empty string */
  22. for (len = 2; len <= str.size(); len++) /* O(n^2) */
  23. { /* when len = 2 --> Special case */
  24. for (i = 0; i+len<=str.size(); i++)
  25. {
  26. j = i + len - 1, diag = 0;
  27. if (str[i] != str[j])
  28. diag = 1;
  29. if( i+1 <= j-1 ) /* at least j-i > 1 */
  30. A[i][j] = min(A[i+1][j]+1, A[i][j-1]+1, A[i+1][j-1]+diag);
  31. else /* Undefined Stage (Not in base case) i = j+1 */
  32. A[i][j] = diag; /* j=i+1Tow char string */
  33. }
  34. }
  35. cout<<"Case "<<k<<": "<<A[0][str.size()-1]<< "\n";
  36. }
  37. return 0;
  38. }
  39. // by zqiceberg
  40. #include <bits/stdc++.h>
  41. using namespace std;
  42. const int N = 1010;
  43. int T, idx;
  44. int dp[N][N];
  45. char s[N];
  46. int main()
  47. {
  48. cin >> T;
  49. while (T--){
  50. scanf("%s", s);
  51. int LEN = strlen(s);
  52. for (int i = 0; i < LEN; i++) dp[i][i] = 0;
  53. for (int len = 2; len <= LEN; len++)
  54. for (int i = 0; i + len <= LEN; i++){
  55. int j = i + len - 1;
  56. int flag = 0;
  57. if (s[i] != s[j]) flag = 1;
  58. if (i + 1 <= j - 1){
  59. dp[i][j] = dp[i + 1][j - 1] + flag;
  60. dp[i][j] = min(dp[i][j], dp[i + 1][j] + 1);
  61. dp[i][j] = min(dp[i][j], dp[i][j - 1] + 1);
  62. }
  63. else dp[i][j] = flag;
  64. }
  65. printf("Case %d: %d\n", ++idx, dp[0][LEN - 1]);
  66. }
  67. return 0;
  68. }

Tips: 对比一下线性dp的 最短编辑距离 edit distance

例题代码

  1. // Cutting Sticks
  2. #include <bits/stdc++.h>
  3. using namespace std;
  4. const int N = 55;
  5. int a[N], l, n;
  6. int mem[N][N];
  7. int best(int i, int j)
  8. {
  9. if (j - i <= 1) return 0;
  10. if (j - i == 2) return a[j] - a[i];
  11. int &ret = mem[i][j];
  12. if (ret != 0x3f3f3f3f) return ret;
  13. for (int k = i + 1; k < j; k++){
  14. ret = min(ret, best(i, k) + best(k, j) + a[j] - a[i]);
  15. }
  16. return ret;
  17. }
  18. int main()
  19. {
  20. while (cin >> l){
  21. if (l == 0) break;
  22. cin >> n;
  23. for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
  24. a[0] = 0, a[n + 1] = l;
  25. memset(mem, 0x3f, sizeof mem);
  26. printf("The minimum cutting is %d.\n", best(0, n + 1));
  27. }
  28. return 0;
  29. }
  1. // Optimal Array Multiplication Sequence
  2. #include <bits/stdc++.h>
  3. using namespace std;
  4. typedef pair<int, int> PII;
  5. const int N = 20, INF = 0x3f3f3f3f;
  6. PII a[N];
  7. int path[N][N];
  8. int n, idx;
  9. int mem[N][N];
  10. void print(int i, int j)
  11. {
  12. if (j == i){
  13. printf("A%d", i);
  14. return ;
  15. }
  16. if (j - i == 1){
  17. printf("(A%d x A%d)", i, j);
  18. return ;
  19. }
  20. int k = path[i][j];
  21. printf("(");
  22. print(i, k);
  23. printf(" x ");
  24. print(k + 1, j);
  25. printf(")");
  26. }
  27. int best(int i, int j)
  28. {
  29. if (j - i == 0) return 0;
  30. if (j - i == 1) return mem[i][j] = a[i].first * a[i].second * a[j].second;
  31. if (mem[i][j] != INF) return mem[i][j];
  32. int &ret = mem[i][j];
  33. for (int k = i; k < j; k++){
  34. int t = best(i, k) + best(k + 1, j) + a[i].first * a[k].second * a[j].second;
  35. if (t < ret){
  36. ret = t;
  37. path[i][j] = k;
  38. }
  39. }
  40. return ret;
  41. }
  42. int main()
  43. {
  44. while (cin >> n && n){
  45. idx++;
  46. for (int i = 1; i <= n; i++){
  47. cin >> a[i].first >> a[i].second;
  48. }
  49. memset(mem, 0x3f, sizeof mem);
  50. best(1, n);
  51. printf("Case %d: ", idx);
  52. print(1, n);
  53. puts("");
  54. }
  55. return 0;
  56. }