考研机试动态规划 线性DP

参考文章:动态规划入门闫氏DP分析法,从此再也不怕DP问题!_哔哩哔哩_bilibili

动态规划 就是 : 给定一个问题,我们把它拆成一个个子问题 ,直到子问题可以直接解决。然后把子问题的答案保存起来,以减少重复计算(记忆化搜索)。再根据子问题答案反推,得出原问题解的一种方法.

主要方法:

1.设计一个数据结构,记录不同规模问题的答案

2.数据结构采用从小到大的生成方式去生成

一般流程: 1.大问题到小问题的拆分 2.找到最小问题 3.记录最小问题,回推到大问题

动态规划入门思路: dfs暴力 --- 记忆化搜索 --- 递推

1dfs > 2记忆化搜索 > 3逆序递推 > 4顺序递推 > 5优化空间

题目:821. 跳台阶 - AcWing题库

记忆化搜索 == dfs暴力+记忆存储重复结果

递推 == dfs的向下递归的公式 递推的边界为:递归的边界

题目:821. 跳台阶 - AcWing题库

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N= 100;
int n;
int f[N];
//递推公式
int main(){
    scanf("%d",&n);
    f[1]=1,f[2] =2;
    if(n==1||n==2){
        f[1]=1,f[2]=2;
        printf("%d\n",f[n]);
        return 0;
    }
    for(int i = 3;i<=n;i++){
        f[i] = f[i-1]+f[i-2];
    }
    printf("%d\n",f[n]);
    return  0;
}//dp递推

2.分苹果3428. 放苹果 - AcWing题库

思路:动态规划

把n 个苹果放到m 个盘子当中,去除重合的情况

数组f[i][j] 表示 把i个苹果放到j个盘子当中 f[0][0] = 1,表示空拆分,递推的起点

分情况:

1.如果i>=j(也就是苹果的数量大于盘子数量) 则: 先把每个盘子都放一个苹果 ,然后剩下i-j个苹果在放随便放j个盘子当中 f[i][j] = f[i][j-1] +f[i-j][j]; j-1表示方案数减1,f[i-j][j]表示还剩下i-j个元素, 有j种方案;

2.如果苹果数量少于盘子数量 ,那么就只有苹果数量这么多种方法 f[i][j] = f[i][i]

cpp 复制代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 15;
int f[N][N];//全局定义数组,不用初始化
int main(){
    int n,m;
    while(cin>>n>>m){
        f[0][0]=1;//不进行拆分也是一种方案,是所有分拆方案的 "起点" 和 "边界条件"
       // memset(f,1,sizeof f);
        for(int i = 0;i<=n;i++){
            for(int j =0;j<=m;j++){
                if(i>=j)
                    f[i][j] = f[i][j-1]+f[i-j][j];//当苹果的数量大于盘中的时候
                    //可以把每个盘子都放一遍, 剩下的苹果可以放任意的j个盘子
                else
                    f[i][j] = f[i][i];//苹果小于盘子时,只能放苹果数量这么多方案
            }
        }
        printf("%d\n",f[n][m]);//解决把n个苹果放到m个盘子中的方案数量
    }
    return 0;
}

2.最长公共子序列(一)_牛客题霸_牛客网

最长公共子序列长度(子序列不要求字符连续,但顺序必须一致)

核心思路:动态规划的状态定义和状态转移

**1.**dp[i][j] 表示 s1 字符串前i个字符和s2字符串前j个字符的最长公共子序列的长度

2.二维数组初始化空串和任何字符串的公共子序列只能是空串,长度自然为 0

for(int i = 0;i<=n;i++) dp[i][0] = 0; // s2前0个字符(空串),和任意s1前i个字符的LCS长度为0 for(int j = 0;j<=m;j++) dp[0][j] = 0; // s1前0个字符(空串),和任意s2前j个字符的LCS长度为0

3.状态转移:分两种情况:1.字符匹配相等(dp[i][j] = dp[i-1][j-1]+1;因为字符前i-1和j-1个相匹配,后面进行,在原基础上长度加1 ), 2. 字符匹配不相等(不能直接加上去,而是去除s1,s2的当前最大值的字符)

cpp 复制代码
if(s1[i-1]==s2[j-1])
    dp[i][j] = dp[i-1][j-1]+1;  // 字符相等的情况
else
    dp[i][j] = max(dp[i-1][j], dp[i][j-1]); // 字符不相等的情况

4.dp[n][m] 长度为n的字符串s1和长度为m的字符串s2的长度的最长公共子序列的长度大小

cpp 复制代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1020;
int dp[N][N];
int n,m;
char s1[N],s2[N]; 
int main(){
	cin>>n>>m;
	cin>>s1>>s2;
    //二维数组初始化
	for(int i = 0;i<=n;i++)
		dp[i][0] = 0;
	for(int j = 0;j<=m;j++)
		dp[0][j] = 0;
	//匹配的条件
	for(int i = 1;i<=n;i++){
		for(int j = 1;j<=m;j++){
			if(s1[i-1]==s2[j-1])
				dp[i][j] = dp[i-1][j-1]+1;
			else
				dp[i][j] = max(dp[i-1][j],dp[i][j-1]); 
		}
	}
    //打印
	cout<<dp[n][m]; 
	return 0;
} 

附件问题: 然后输出最短公共子序列是什么:

思路:定义一个lcs字符串 进行拼接 如果s1 == s2 则拼接,并且i--,j--,如果不相等 则 将dp长度大的减1, 最后将lcs使用reverse反转一下

cpp 复制代码
//逻辑实现 从子串后面往前遍历 ,最后做一个反转reverse 
	int i = n,j=m;
	while(i>0&&j>0){
		if(s1[i-1]==s2[j-1]){
				LCA+=s1[i]-1;
				i--;j--;
		}
		else if(dp[i-1][j]>dp[i][j-1])//dp长度比较 ,如果i的长度大,则减1 ,否则 j 减1 
			i--;
		else
			j--;
	} 
 	reverse(LCS.begin(), LCS.end()); 
	cout<<"最长公共子序列长度"<<dp[n][m]<<endl;
	cout<<"最长公共子序列为:"<<LCS; 
    

2.求解最长公共子串3508. 最长公共子串 - AcWing题库

使用二维数组空间大 则使用一维数组替换的核心思路:"复用" 空间

既然计算 dp[i][j] 只需要 dp[i-1][j-1],我们可以用一维数组 dp[j] 来 "复用" 存储

  • 初始时,dp[j] 存储的是上一行(i-1 行) 的所有结果;
  • 计算当前行(i 行)的 dp[j] 时,我们需要的是上一行的 dp[j-1](即原 dp[i-1][j-1]);
cpp 复制代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cctype> //isalpha 的头文件
using namespace std;
const int N = 10010;
int dp[N];
char s1[N],s2[N];
int curmax = 0;
int main(){
    cin>>s1>>s2;
    int s1len = strlen(s1);
    int s2len = strlen(s2);
    
    memset(dp,0,sizeof dp);//初始化
    for(int i =1 ;i<=s1len;i++){
        for(int j = s2len;j>0;j--){//倒序输出
            if(s1[i-1]==s2[j-1]&&isalpha(s1[i-1])&&isalpha(s2[j-1])){
                dp[j] =dp[j-1]+1;//用一维数组记录
                curmax = max(dp[j],curmax);//取最大值
            }
            else
                dp[j] = 0;
        }
    }
   
    printf("%d\n",curmax);
    
    return 0;
}
  1. 最大序列和3393. 最大序列和 - AcWing题库

思路:

dp 情况:

case1 :

如果 d[i-1] <=0 则下一位d[i] = s[i-1]返回是一个位置'

case2

如果 d[i-1]>0 则下一位d[i] = s[i-1] +d[i-1]直接加上其和

使用curmax 进行存放每次的最大值

cpp 复制代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const long long  N = 100000010;
long long dp[N];//dp[i] 表示以s[i-1]为结尾的连续子数组的最大和 
int n;
long long s[N];
int main(){
	scanf("%d",&n); 
	for(int i = 0;i<n;i++)
		scanf("%lld",&s[i]);//输入数字 
	dp[1] = s[0];//dp[1] 以s[0](第一个元素)结尾的最大子数组和就是它本
	long long curmax = dp[1];//初始化 
	//状态转移 
	for(int i = 2;i<=n;i++){
		if(dp[i-1]<=0)
			//case1 dp[i-1]<=0 会拉低和的值,不如重新开始 
			dp[i] = s[i-1];
		else
			dp[i] = s[i-1] + dp[i-1];//接上它能让当前和更大,直接累加
			
		curmax = max(curmax,dp[i]); 
	}
	cout<<curmax;
	return 0;
} 

4.最大上升子序列和最大上升子序列和_牛客题霸_牛客网

思路:

dp状态转移条件,dp数组是以num[i]为结尾的数组,前面上升序列和

if(num[i]>num[j])

dp[i] = max(dp[i],dp[j]+num[i]);//记录更新大小的值 dp[j] + num[i]表示接上这个数之后,

在所有能接的 j 里,选一个让 dp[i] 最大的结果,保证 dp[i] 始终是 "以 num[i] 结尾的最大和"。

cpp 复制代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std; 
const int N = 1010;
int dp[N];//dp记录 
int num[N];

int n;

int main(){
	while(cin>>n){
		for(int i = 0;i<n;i++)
			scanf("%d",&num[i]);//初始化数组 
			
		int ans= 0; //初始ans 
		for(int i = 0;i<n;i++){//遍历循环 
			dp[i] = num[i];//初始化 
			for(int j = 0;j<i;j++){
				if(num[i]>num[j]){//判断条件 
					dp[i] = max(dp[i],dp[j]+num[i]);//记录更新大小的值 
				}
				ans = max(ans,dp[i]); //更新最值 
			}
		}
		cout<<ans<<endl; 
	}
	return 0;
} 
相关推荐
listhi5202 小时前
两台三相逆变器并联功率分配控制MATLAB实现
算法
Evand J2 小时前
【IMM】非线性目标跟踪算法与MATLAB实现:基于粒子滤波的交互式多模型,结合CV和CT双模型对三维空间中的机动目标进行高精度跟踪
算法·matlab·目标跟踪·pf·粒子滤波·imm·多模型
重生之后端学习2 小时前
64. 最小路径和
数据结构·算法·leetcode·排序算法·深度优先·图论
We་ct2 小时前
LeetCode 212. 单词搜索 II:Trie+DFS 高效解法
开发语言·算法·leetcode·typescript·深度优先·图搜索算法·图搜索
样例过了就是过了2 小时前
LeetCode热题100 路径总和 III
数据结构·c++·算法·leetcode·链表
lxh01132 小时前
函数防抖题解
前端·javascript·算法
再难也得平2 小时前
力扣41. 缺失的第一个正数(Java解法)
数据结构·算法·leetcode
颜酱2 小时前
环检测与拓扑排序:BFS/DFS双实现
javascript·后端·算法
IronMurphy2 小时前
【算法二十】 114. 寻找两个正序数组的中位数 153. 寻找旋转排序数组中的最小值
java·算法·leetcode