【算法笔记】两道DP水题(区间DP,线性DP)

在acwing上学习算法的一点思考与总结


石子合并

分析:当只有两堆石子的时候最小代价就是他俩的和。当如果由三堆石子时有两种方案(先合右边两堆和先合左边两堆),然后比较两个方案最小代价和。也就是说不管他有几堆石子,我们在处理的时候只处理相邻两堆的石子(两堆石子合并成后变成一堆)。演示过程如下

状态转移:自然而然我们的状态就可以写成f[ i ][ j ],表示区间 i ~ j 里的石子的数量,然后状态转移方程要做的就是不断从小到大变化这个区间范围,并更新区间内石子的最代价

遍历所有区间:为了遍历所有的区间范围,我们需要三个量。第一个是区间的[ i , j ]的长度len(即有多少堆石子)第二个是区间的起点 i ,第三个是区间的终点 j 。实际上知道起点位置和区间长度就可以求出终点位置。所以我们设置两层循环,第一层遍历区间长度,第二层遍历起点。

为什么要用到前缀和:这个稍微模拟一下合并的过程就知道为什么要用到前缀和。简单来说,最后计算出来的总的代价和是每堆石子代价不断累积的结果。

贴个手写模拟的草图,方便日后复习

#include<iostream>

using namespace std;
const int N = 310;
int n;
int s[N];
int f[N][N];

int main()
{
    cin>>n;
    
    for(int i = 1; i <= n; i ++) cin>>s[i];
    
    for(int i = 1; i <= n; i ++) s[i] += s[i-1]; //求前缀和,用于计算第i~j堆石子的代价和
    
    for(int len = 2; len <= n; len ++) //遍历区间长度
        for(int i = 1; i + len -1 <= n; i ++) //如果终点超过了石子堆数,则退出循环
        {    
            int j = i + len - 1;
            f[i][j] = 1e8;
            for(int k = i; k < j; k ++) //k可以理解为一个挡板,挡板左边的合并成一堆,挡板右边的合并成一堆,这样子就变成相邻两堆合并的问题
                f[i][j] = min(f[i][j], f[i][k] + f[k+1][j] + s[j] - s[i-1]);
        }    
    cout<<f[1][n];
}

最长上升子序列 

分析:在一个数组中一个数字w[ i ]的最长上升子序列,可以由前面i - 1个数字的最长上升子序列求得,也就是说假设在1~ i - 1中存在一个数字k, 其大小小于第i个数,则调取以k数字结尾的最长上升子序列加上1

状态转移:用 f [ i ]来表示以第w[i]个数结尾的最长上升子序列。那么状态转移方程就是f[ i ] = max(f[ i ], f[ j ] + 1)(j要遍历第1~i-1个数字的最长上升子序列),取max来进行更新。

#include<iostream>

using namespace std;
const int N =1010;
int w[N], f[N];
int n;

int main()
{
    cin>>n;
    for(int i = 0; i < n; i ++) cin>>w[i];
    for(int i = 0; i < n; i ++)
    {    
        f[i] = 1;
        for(int j = 0; j < i; j++)
            if(w[j] < w[i])
                f[i] = max(f[i], f[j] + 1);
    }
    int res = 1;
    for(int i =0; i < n; i ++) res = max(res, f[i]);
    cout<<res;
    return 0;
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值