动态规划模型总结

本文介绍了动态规划在数学和计算机科学中的应用,重点探讨了线性DP、最长子序列和问题、背包DP以及区间DP的原理和实例,强调了状态转移方程的构建和优化策略。
摘要由CSDN通过智能技术生成

动态规划

动态规划(Dynamic programming)是一种在数学、计算机科学和经济学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。 动态规划常常适用于有重叠子问题和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法。 动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再合并子问题的解以得出原问题的解。 通常许多子问题非常相似,为此动态规划法试图仅仅解决每个子问题一次,从而减少计算量: 一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。

主要有下面几个DP类型:线性DP(股票系列、最长子序列和(见简单DP))、区间DP(最长回文子串)、背包DP、状态压缩DP、递归DP(爬楼梯、斐波那契数)、树状DP等
这里只写了线性DP、区间DP、背包DP这三个dp,剩下几个万能的百度也能找到

线性DP

虽然说是线性,但是实际上看起来也没有这么线性,主要是如何定义状态方程之间的联系,找到状态转移方程,找到状态转移方程之后,写出完整的代码就简单了。状态转移之间一般是利用上一个或者给定的区分值进行遍历,对于循环的正序或者倒序,主要看定义的状态转移方程是否还利用上一状态的值

最长子序列和问题

dp[i]表示以第i个数结尾的最长子序列和
转移方程:
//要么相连,要么自己一个数组
dp[i]=max{dp[i-1]+num[i],dp[i]}
最长子序列问题
#include<iostream>
#include<algorithm>

using namespace std;
const int N=1010;
int dp[N];
int f[N];
int max1;
int main()
{
    int n;
    cin>>n;
    
    for(int i=1;i<=n;i++) cin>>f[i];
    for(int i=1;i<=n;i++)
    {
        dp[i]=1;
        //以i结尾的最大序列长度
        for(int j=0;j<=i;j++)
        {
            if(f[i]>f[j]) dp[i]=max(dp[j]+1,dp[i]);
        }
        max1=max(max1,dp[i]);
    }
    cout<<max1<<endl;
    return 0;
}

背包DP

注意里面的改变是减去重量而不是减去1或者加上1

01背包问题

f[i][j]//j代表容量,i代表第几个物品
    	//f[i][j]代表再第i个物品截止,在能承载重量为j时的最大值
   
 
   /*转移方程:
   第一种:不放入背包,则f[i][j]=f[i-1][j]
   第二章:放入背包,如果放的下:
   f[i][j]=f[i-1][j-w[i]]+v[i]
   如果放不下:f[i][j]=f[i-1][j]
   两者进行比较,看看时放入大还是不放入大
   */
    
    //为什么是从j=v开始,j--(只有一维数组才是)
    //因为如果是正序,当更新到f[3]时,f[2]已经更新了,
    //不再是我们需要的f[2]

改编之AcWing 1047. 糖果(深入理解不合法状态) - AcWing](https://www.acwing.com/solution/content/108139/)

问题:

背包问题是不超过;糖果问题是恰好;

在背包问题中,f[0] [j]都是由意义的

在糖果问题中,只有f[0] [j%k]才是有意义的

所以在糖果问题中,下标是要取mod

dp[i][j]//代表前i个物品的总价值%k=j的集合在这里限制是关于%k余数是多少
    
    /*转移方程
    dp(i,j)=max(dp(i−1,j),
    dp(i−1,(((j−w[i])%k)+k)%k) +w[i])
    不选的话:就是前面那个的值
    选的话,就是
    子集合都包括第i个物品,并且前面选择的商品和S满足		(S+a[i])%k=j
    而我们需要从和这个S有关的集合,状态记录的都是%k
	后的结果,S为前i-1个商品的总和,j为前i个商品的余数
	所以S%k=(j−a[i])%k)为上一个状态的值
    */
    
    3.初始化:
dp(0,0)=0,dp(0,i)(i≠0)
都是不合法的状态,所以必须要初始化为负无穷,不然的话可以选择先初始化第一件物品的所有情况,再迭代第二个物品之后 (仔细想想,如果不合法的状态初始化为0了,后面递推的时候,答案就会变大)
    
    4.注意事项:
转移方程中的(j−w[i])%k)可能为负的,必须要将余数变成[0,n−1]之间,所以cpp负数取余要变成(j−w[i])%k+k)%k
    
    5、正逆序问题
    由于它是二维数组,正逆序更新不会影响当前,所以可以正序可以逆序

区间DP

AcWing 1222. 密码脱落 - AcWing

此题是回文串问题,

回文串问题的思路,此问题的目的是两边一起走

如果左右端的值相等那么只需要判断i+1,j-1的是否相等。拆分成了一个小区间。
此题可以运用dfs进行运算,如果左边和右边相等,那么判断i+1,j-1,如果不相等,判断i+1,j与i,j-1;那一边删去更小
 退出条件:1、左边大于右边返回0
    2、f[l][r]中已经判断了最小值是多少,可以直接返回

动态规划求解思路:

dp[i][j]:代表区间[i,j]里去掉字符变成字串str(i,j)的集合
状态转移:
    1、如果两边相等,dp[i][j]=dp[i+1][j-1]
    2、如果两边不相等,dp[i][j]=
    min(dp[i+1][j],dp[i][j-1])+1;
枚举顺序:因为要用到i+1的状态所以i要逆序枚举
   注意:
    当i==j−1且s[i]==s[j]时,这也是回文串,但是dp(i+1,j−1)是不合法的,被声明称了INF.当时,这也是回文串但是不合法的,被声明称了,所以要单独拿出来讨论。
    
    注意其枚举方法,i与j
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值