282. 石子合并

原题链接

传送门
突然发现如果没有一个传送门的话,之后要是想在线评测会比较麻烦,所以还是得加上原题链接

题意

给定多个元素,每一次把可以把相邻的两个元素合并,两个元素合并的和做一个累加,这个累加的结果作为答案,最后数组里面只剩下一个元素,结束操作,求答案的最小值

输入

4
1 3 5 2

输出

22

数据范围

n<=300

代码

#include

using namespace std;

const int N=310;

int a[N];
int s[N];
int dp[N][N];

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    
    int n;
    cin>>n;
    
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        s[i]=s[i-1]+a[i];
    }
    
    for(int len=2;len<=n;len++)
    {
        for(int i=1;i+len-1<=n;i++)
        {
            int l=i,r=i+len-1;
            dp[l][r]=1e8;
            for(int j=l;j<=r;j++)
            {
                dp[l][r]=min(dp[l][r],dp[l][j]+dp[j+1][r]+s[r]-s[l-1]);
            }
        }
    }
    
    cout<<dp[1][n]<<endl;
    
    return 0;
}

总结

这是一道区间dp问题

dp集合表示的是从 i 到 j 的合并方式

总体思路

首先假设是暴力枚举的话,时间复杂度是 ( n - 1 ) ! ,n==300.时间复杂度不符合要求

考虑使用 dp 来进行求解,dp 是一个元素表示一类元素的集合,所以是一种优化方式

状态转移的时候,每一次更新是把相邻的两个元素合并,所以需要枚举分界点,分界点可以是第一个元素一直到倒数第二个元素,如果到最后一个元素就只剩下一个元素,讨论将没有意义

长度是1的话,不需要进行操作,答案是0,全局变量初始值为0,所以长度从2开始变化,一直到最大长度n

然后枚举点,len表示的是数组的长度,i 表示的是数组的起点,起点从1 开始,假设长度是1 ,从1 开始,结束的时候应该还是1 ,但是1+1=2,这里需要注意下,就是加上长度需要减去1才是结尾的元素,靠特殊例子我们可以准确认识到这个问题,这个非常常见,最好记住

i+len-1表示结尾的点

左右边界表示的是把左右边界的元素进行合并,一个区间我们把它分成两个部分,前面一部分取最小值,后面一部分取最小值,然后加上部分和(前缀和处理出来的),部分和表示的是合并的这个过程,不然DP数组没有初始值,不停地状态转移,状态计算也没有用

前缀和比较简单,这里就不再赘述

可以参考这一边博客了解前缀和或者复习前缀和

前缀和

你可能感兴趣的:(动态规划,算法)