线性dp和区间dp


活动地址:优快云21天学习挑战赛

文章目录

前言

1、小介绍

2、例题:最长上升子序列问题

3、例题:最长子序列方案书问题

 


前言

这是鄙人第一次正儿八经的教学文章,还是蛮紧张的,不懂可以留言询问。本文可能适合稍微回些动态规划的人。经典例题还会继续补充。

1、介绍

线性DP是动态规划问题中的一类问题,指状态之间有线性关系的动态规划问题。尤其是最长上升子序列问题,最长公共子序列方案数等问题。其实线性dp无论是几维的,都是要从一个地方入手,以某确定方向获得整个状态空间转移、拓展。每个状态都保留子问题最优解。

2、例题:最长上升子序列问题

题目描述:给定n个数ai,求数值单调递增的子序列的最长长度为多少?

最长上升子序列(一)_牛客题霸_牛客网 (nowcoder.com)

分析:不妨先自己设几个数: 3 1 3 2 4 3。我们可以观察到最长的是1 2 4或1 2 3或1 3 4,大小为3。我们可以发现可以存储以a[i]结尾的状态(长度)。令f【i】表示以a[i]为结尾的最长上升自序列的长度(初始均为1,本身一个数也是序列),从第一个3开始可以获得f:1 1 1 1 2 1,从1开始可以获得f:1 1 2  2  3 3......可以找出状态转移方程:f[i]=max(f[i],f[j]+1)。(可以了解一下闫氏dp法)

代码:

#include<iostream>
using namespace std;
int main()
{
    int a[1010],n,res;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    int f[1010];
    for(int i=1;i<=n;i++)
    {  
        f[i]=1;
        for(int j=1;j<=i;j++)//对比一下之前,看看能不能更新最大值。
//我们要保证每一步做完之后,f[i]中保留的都是以a[i]结尾的最大长度。
        {
            if(a[j]<a[i])
                f[i]=max(f[i],f[j]+1);
        }
        res=max(res,f[i]);
    }
    printf("%d",res);
}

此时,你会发现时间复杂度为o(n2),可用树状数组维护(复杂度为nlog(n))。奈何本人学艺不精,唉。

3、例题:最长子序列方案数量问题

673. 最长递增子序列的个数 - 力扣(LeetCode)

分析:这个题我们需要用到两个状态,故开两个数组,一个存以a【i】结尾的最长子序列长度,一个存以a【i】结尾的最长子序列个数。由于我们要更新g[i],f[i]还是分类进行处理。

class Solution {
public:
    int findNumberOfLIS(vector<int>& nums) {
        int n=nums.size();
        int res=0,maxn=1 ;
        int f[2010];//与上面的f含义相同
        int g[2010];//以a【i】结尾的最长子序列的个数。
        f[0]=g[0]=1;
        for(int i=1;i<n;i++)
        {
             f[i]=1,g[i]=1;
             for(int j=0;j<i;j++)
             {
                 if(nums[j]<nums[i])//仅当前比后小时处理
                {
                    if(f[j]+1>f[i])//当前最长长度不是最大//j比i小f[j]表示以a[j]结尾最长子序列的个数。+1才是当前长度。
                    {
                        
                        f[i]=f[j]+1;
                        g[i]=g[j];//长度变了,个数自然也变了
                    }
                    else if(f[j]+1==f[i])//两个以f[j]结尾的一样长,个数相加
                    g[i]+=g[j];
                }

             }
            if(f[i]>maxn)//求最大的长度
            maxn=f[i];
        }
        for(int i=0;i<n;i++)
       { if(f[i]==maxn)
        res+=g[i];}
       
        return res;
    }
    
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值