动态规划——区间dp

区间dp是dp问题中非常常见的类型,主要涉及类似于小船渡河,石子合并以及多个矩阵连乘等问题。

解决方法也比较固定,先从dp的思想开始说起。dp的灵魂就是累计从小问题的最优解通过某种递归关系,一步步求得大问题的最优解。有点类似递归中归的过程。

这里举一个石子合并的例子,先看题目。

在这里插入图片描述

我们遇到dp问题,先想怎么进行状态表示。我们先用a [N] 数组来装这些石头的重量,如a [i]表示第i个石头的重量。这里我们用一个二维数组 f [i][j] ,表示第 i 个石头到第 j 个石头合并后的最小代价。好了,现在就该推递归式了。我们假设现在有两堆石头堆,分别是 f [l][k] f[k + 1][r] ,根据题目的要义我们得出递归式为: f[l][r] = f[l][k] + f[k + 1][r] + a[l] + a[l + 1] + … + a[r]

可以看到,其本质就是在区间 i — j 中找到一个分割点 k , 再将分割出来的两个区间 f [l][k] 以及 f [k + 1][r] 进行合并。由于 k 的值在l — r 之间,因此需要逐个列举并求出合并的最小值。根据题目意思,f [1][n] 即为包含了1 到 第n个石头的最优解。好了,现在该上代码了,可以看看代码中的解析部分。

在这里插入图片描述

#include<iostream>
#include<algorithm>

using namespace std;

const int N = 310;
int n;
int f[N][N];
int s[N];

int main()
{
    
    
    scanf("%d" , &n );

    for(int i = 1; i <= n; i++ ) scanf("%d" , &s[i] );

    //利用前缀和 
    for(int i = 1 ; i <= n ; i++ ) s[i] += s[i - 1];

    for(int len = 2 ; len <= n; len++ ) //划分区间,从2开始一直到n 
    {
    
    
        for(int i = 1 ; i + len - 1 <= n ; i++) //先从1开始作为区间的左端点
        {
    
    
            int l = i , r = l + len - 1;
            f[l][r] = 1e8;
            for(int k = l ; k < r ; k++ ) //在已有的区间中再设立分割点k,利用前缀和进行解决 
                f[l][r] = min( f[l][r] , f[l][k] + f[k + 1][r] + s[r] - s[l - 1] );
        }
    }

    cout << f[1][n] << endl;

    return 0;
 } 

作者:chenxuanqi6@163.com
链接:https://www.acwing.com/activity/content/code/content/523537/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

猜你喜欢

转载自blog.csdn.net/MrChen666/article/details/109084516
今日推荐