0x53. 动态规划 - 区间DP(习题详解 × 8)

Posted 繁凡さん

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了0x53. 动态规划 - 区间DP(习题详解 × 8)相关的知识,希望对你有一定的参考价值。

整理的算法模板合集: ACM模板

点我看算法全家桶系列!!!

实际上是一个全新的精炼模板整合计划


0x53. 动态规划 - 区间DP

区间DP是线性DP的一种特殊模型,以区间长度作为DP的阶段,区间左右端点作为维度。一个状态由若干个比它更小且包含于它的区间所代表的状态转移而来。

Template 石子合并

AcWing 282

设有 N N N 堆石子排成一排,其编号为 1 , 2 , 3 , … , N 1,2,3,…,N 1,2,3,,N

每堆石子有一定的质量,可以用一个整数来描述,现在要将这 N N N 堆石子合并成为一堆。 每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。找出一种合理的方法,使总的代价最小,输出最小代价。

1 ≤ N ≤ 300 1≤N≤300 1N300

Solution

f [ i , i ] f[i, i] f[i,i] 表示区间合并 [ l , r ] [l, r] [l,r] 的最小代价

f [ l , r ] = min ⁡ l ≤ k < r { f [ l , k ] + f [ k + 1 , r ] } + ∑ i = l r a i f[l, r] = \\min_{l\\le k <r}\\{f[l, k] +f[k + 1, r]\\} +\\sum_{i = l}^{r}a_i f[l,r]=lk<rmin{f[l,k]+f[k+1,r]}+i=lrai

初始化: ∀ i ∈ [ 1 , n ] , f [ i ] [ i ] = 0 \\forall i\\in [1, n],f[i][i] = 0 i[1,n],f[i][i]=0,其余为 INF。
目标: f [ 1 , n ] f[1, n] f[1,n]

直接将 l , r l, r l,r 作为 DP 的阶段转移的话, 无法保证计算 l ⋯ r l\\cdots r lr 的时候,更小的阶段 l , k l, k l,k 已经被算过了,所以我们应该以区间长度 l e n len len 为阶段进行转移。

Code

#include <bits/stdc++.h>
using namespace std; 
const int N = 300 + 7;

int n, m, a[N];
int f[N][N], sum[N];

int main()
{
    memset(f, 0x3f, sizeof f);
    scanf("%d", &n);
    for (int i = 1; i <= n; ++ i) 
        scanf("%d", &a[i]), f[i][i] = 0, sum[i] = sum[i - 1] + a[i];
        
    for (int len = 2; len <= n; ++ len) {//阶段
        for (int l = 1; l + len - 1 <= n; ++ l) {//状态
            int r = l + len - 1;//状态
            for (int k = l; k < r; ++ k)//决策 
                f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r]);
            f[l][r] += sum[r] - sum[l - 1];
        }
    }
    cout << f[1][n] << '\\n';
    return 0;
}

Problem A. 最优矩阵链乘

一个 n × m n\\times m n×m 的矩阵由 n n n m m m 列共 n × m n\\times m n×m 排列而成。两个矩阵 A 和 B 可以相乘当且仅当 A 的列数等于 B 的行数。一个 n × m n\\times m n×m 的矩阵乘 m × p m\\times p m×p 的矩阵,运算量为 n × m × p n\\times m\\times p n×m×p

矩阵乘法不满足分配律,但满足结合律。因此 A × B × C A\\times B\\times C A×B×C 既可以按顺序 ( A × B ) × C (A\\times B)\\times C (A×B)×C 也可以按 A × ( B × C ) A\\times (B\\times C) A×(B×C) 来进行。假设 A , B , C A,B,C A,B,C 分别是 2 × 3 , 3 × 4 , 4 × 5 2\\times 3,3\\times 4,4\\times 5 2×3,3×4,4×5 的,则 ( A × B ) × C (A\\times B)\\times C (A×B)×C 运算量是 2 × 3 × 4 + 2 × 4 × 5 = 64 2\\times 3\\times 4+2\\times 4\\times 5=64 2×3×4+2×4×5=64 A × ( B × C ) A\\times (B\\times C) A×(B×C)的运算量是 3 × 4 × 5 × 2 × 3 × 5 = 90 3\\times 4\\times 5\\times 2\\times 3\\times 5=90 3×4×5×2×3×5=90 .显然第一种顺序节省运算量。

给出 n − 1 n-1 n1 个矩阵组成的序列,设计一种方法把他们依次乘起来,使得总的运算量尽量小。假设第 i i i 个矩阵 A [ i ] A[i] A[i] P [ i − 1 ] × P [ i ] P[i-1]\\times P[i] P[i1]×P[i] 的(意味着这些矩阵可以按顺序链乘,所以只需要考虑区间相乘中区间的选取)。

POJ1651 Multiplication Puzzle

乘法游戏是在一行牌上进行的。每一张牌包括了一个正整数。在每一个移动中,玩家拿出一张牌,得分是用它的数字乘以它左边和右边的数,所以不允许拿第1张和最后1张牌。最后一次移动后,这里只剩下两张牌。你的目标是使得分的和最小。

Solution

f [ i , j ] f[i,j] f[i,j] 表示 A[i], A[i+1], ..., A[j] 乘起来的最少运算数。

转移方程:
f [ i , j ] = min ⁡ { f [ i , j ] , f [ i , k ] + f [ k + 1 , j ] + a [ i ] × a [ k + 1 ] × a [ j + 1 ] ) } , i ≤ k < j f[i,j]=\\min\\{f[i,j],f[i,k] + f[k+1,j]+ a[i]\\times a[k + 1] \\times a[j + 1])\\},i\\le k<j f[i,j]=min{f[i,j],f[i,k]+f[k+1,j]+a[i]×a[k+1]×a[j+1])},ik<j

初始化 : f [ i , i ] = 0 f[i, i] = 0 f[i,i]=0

目标: f [ 1 ] [ n − 1 ] f[1][n - 1] f[1][n1]

Code

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cstring>

using namespace std;
typedef long long ll;
const int N = 2007, M = 5000007, INF = 0x3f3f3f3f;

int n, m;
int f[N][N];
int a[N];

int main()
{
    while(scanf("%d", &n) != EOF && n){
        memset(f, 0x3f, sizeof f);
        for(int i = 1; i <= n; ++ i){
            scanf("%d", &a[i]);
            f[i][i] = 0;
        }

        for(int len = 2; len < n; ++ len){
            for(int i = 1; i <= n - len; ++ i){
                int j = i + len - 1;//注意再 -1 因为三个数才能组成两个可乘矩阵相乘
                f[i][j以上是关于0x53. 动态规划 - 区间DP(习题详解 × 8)的主要内容,如果未能解决你的问题,请参考以下文章

0x54. 动态规划 - 树形DP(习题详解 × 12)

0x56. 动态规划 - 状态压缩DP(习题详解 × 7)

0x52. 动态规划 - 背包(习题详解 × 19)

《算法零基础100例》(第100例) 动态规划 - 区间DP

动态规划:区间DP问题零神基础精讲

0x55. 动态规划 - 环形与后效性处理(例题详解 × 6)