动态规划及其优化

Posted solemntee

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划及其优化相关的知识,希望对你有一定的参考价值。

(转移)和结尾元素有关的dp

1、只最后一个元素相关

问题1:最长上升连续子串(连续区间 d p dp dp

对于连续区间之间的 d p dp dp,转移的对象极为清晰,不多赘述
设置 d p [ i ] dp[i] dp[i]表示以 a [ i ] a[i] a[i]右端点的最长上升子串长度,如果 a [ i ] < = a [ i − 1 ] a[i]<=a[i-1] a[i]<=a[i1] d p [ i ] = 0 dp[i]=0 dp[i]=0,否则 d p [ i ] = d p [ i − 1 ] + 1 dp[i]=dp[i-1]+1 dp[i]=dp[i1]+1

问题2:(子序列dp)

Meeting Time II
二维平面上有n个站点,你需要按顺序经过(从站点1到站点2到站点3…)这些站点,但你可以选择k个站点进行跳过,求最短路

设置 d p [ i ] [ j ] dp[i][j] dp[i][j]表示以 i i i节点作为右端点且已经跳跃了 j j j次的最短路,这样就可以通过枚举上一个节点 O ( n 3 ) O(n^3) O(n3)转移

(扯开去讲一下,因为区间之间的转移和最后一个元素是什么有关,所以对于每一个转移知道状态的结尾元素,设置以 i i i作为右端点是非常有必要的,如果这边状态中 i i i含义是前 i i i个的话就没办法转移了,因为对于 d p [ i ] [ 1 ] dp[i][1] dp[i][1]你不知道跳过了哪个节点。但有的时候对于子序列计数问题,我们会将状态设置为前 i i i个的数量前缀和,因为这个时候就我们不需要知道最后一个元素具体位置,只需要知道某结尾序列数量即可)

问题3、最长上升子序列(子序列dp的优化)

同样是寻找上一个端点,不同于 O ( n ) O(n) O(n)暴力的寻找,在 L I S LIS LIS里面我们通过二分优化了寻找上一个元素的时间复杂度

2、只和最后k个元素相关

考虑状压最后 k k k个元素的形态,花 O ( 2 k ) O(2^k) O(2k)的复杂度进行转移

3、只和最后一个连续段长度相关,且间隔(gap)固定

(间隔不定的话就只能二进制枚举了)
总体来讲思路都是极为相似的,通过设置 d p dp dp状态为右端点来进行转移
设置 d p [ i ] [ j ] dp[i][j] dp[i][j]为以 i i i作为右端点的,最后一个连续段长度为 j j j d p dp dp值,考虑转移
d p [ i + g a p + k ] [ k ] = f ( d p [ i ] [ j ] , k ) dp[i+gap+k][k]=f(dp[i][j],k) dp[i+gap+k][k]=f(dp[i][j],k)

前缀和优化dp

Armchairs
题目的意思是有 n n n个座位 m m m个人 ( m < n / 2 ) (m<n/2) m<n/2坐在一些位置上,现在要求原来这些坐着人的位置都空出来,让这些人坐在其他座位上,问移动距离总和的最小值。
d p [ i ] [ j ] dp[i][j] dp[i][j]表示第i个人坐到第j个原来空着的位置,并且前i个人完全匹配的最小移动距离。
我们来考虑转移
d p [ i ] [ j ] = m i n k = 1 , 2 , . . , j − 1 ( d p [ i − 1 ] [ k ] ) + a b s ( d i s 1 [ i ] − d i s 0 [ j ] ) dp[i][j]=min_{k=1,2,..,j-1}(dp[i-1][k])+abs(dis1[i]-dis0[j]) dp[i][j]=mink=1,2,..,j1(dp[i1][k])+abs(dis1[i]dis0[j])
我们想到了一个O(n^3)的dp,然后我们发现 m i n k = 1 , 2 , . . , j − 1 ( d p [ i − 1 ] [ k ] ) min_{k=1,2,..,j-1}(dp[i-1][k]) mink=1,2,..,j1(dp[i1][k])
是可以 O ( n ) O(n) O(n)预处理的,那么这个 d p dp dp就被优化掉了一维,变成了 O ( n 2 ) O(n^2) O(n2)



    #include<bits/stdc++.h>
    using namespace std;
    int cnt1=0,cnt0=0;
    int dis1[5005],dis0[5005];
    int dp[5005][5005];
    int minn[5005];
    int main()
    {
        int n;
        scanf("%d",&n);
     
        for(int i=1;i<=n;i++)
        {
            int t;
            scanf("%d",&t);
            if(t==1)dis1[++cnt1]=i;
            if(t==0)dis0[++cnt0]=i;
        }
        for(int i=0;i<=cnt1;i++)
        for(int j=0;j<=cnt0;j++)
        dp[i][j]=1e9;
     
     
        for(int i=1;i<=cnt0;i++)dp[1][i]=abs(dis1[1]-dis0[i]);
        for(int j=0;j<=cnt0;j++)minn[j]=1e9;
        for(int j=1;j<=cnt0;j++)minn[j]=min(minn[j-1],dp[1][j]);
     
        for(int i=2;i<=cnt1;i++)
        {
     
            for(int j=i;j<=cnt0;j++)if(minn[j-1]!=1e9)dp[i][j]=minn[j-1]+abs(dis1[i]-dis0[j]);
            for(int j=0;j<=cnt0;j++)minn[j]=1e9;
            for(int j=1;j<=cnt0;j++)minn[j]=min(minn[j-1],dp[i][j]);
     
        }
     
        int minn=1e9;
     
        if(cnt1==0)printf("0\\n");
        else
        {
            for(int i=1;i<=cnt0;i++)
            {
                minn=min(minn,dp[cnt1][i]);
     
            }
     
            printf("%d\\n",minn);
        }
        return 0;
    }

Matrix
题目的意思是在这个矩阵里面选取一块楼梯形的区域,使得区域和最大。
我们设状态 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k] i i i表示第 i i i列, j j j表示第 j j j列的高度, k k k表示总总共选取的方格数。
考虑转移
d p [ i ] [ j ] [ k ] = m a x t > = j ( d p [ i − 1 ] [ t ] [ k − j ] ) dp[i][j][k]=max_{t>=j}(dp[i-1][t][k-j]) dp[i][j][k]=maxt>=j(dp[i1][t][kj])
复杂度为 O ( n 4 ) O(n^4) O(n4)
然后我们发现这个 m a x max max值满足递推关系
m a x t > = j + 1 ( d p [ i − 1 ] [ t ] [ k − j ] ) = m a x ( m a x t > = j ( d p [ i − 1 ] [ t ] [ k − j ] ) , d p [ i − 1 ] [ j ] [ k − j ] ) max_{t>=j+1}(dp[i-1][t][k-j])=max(max_{t>=j}(dp[i-1][t][k-j]),dp[i-1][j][k-j]) maxt>=j+1(dp[i1][t][kj])=max(maxt>=j(dp

以上是关于动态规划及其优化的主要内容,如果未能解决你的问题,请参考以下文章

五大经典算法-动态规划 及其算法应用

动态规划及其应用

动态规划问题3--多重背包

动态规划问题3--多重背包

信息学干货-动态规划模型优化及解题思路(上)

动态规划整数划分及其变种