当前位置:   article > 正文

数据结构与算法之动态规划_3、设计一个动态规划算法,找到字符串t[1..n]中前向和后向相同的最长连续子串。前

3、设计一个动态规划算法,找到字符串t[1..n]中前向和后向相同的最长连续子串。前

经典题目

以打家劫舍这个例子开始
在这里插入图片描述
其实这个题目的意思就是:给你一个数组,拿取不相邻的数组的值,求所有可能拿取的值中的最大值
题目分析:

递归方式

就是自己调用自己,一种暴力枚举的方式。所谓暴力是没有经过任何的优化,枚举了所有的情况的方式

//从后向前遍历做法
class Solution {
public:
	int help(int index){
		if(index==0)return nums[0];
		if(index==1)return max(nums[0],nums[1]);
		int use=help(index-2)+nums[index];//选了当前这个数
		int ofuse=help(index-1);//不选当前这个数
		return max(use,ofuse);
}
    int rob(vector<int>& nums) {
    if(nums.size()==0)return 0;
    return help(nums.size()-1);
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

记忆化递归

开始真正的动态规划之前,先记录下记忆化搜索的这种方式。
所谓记忆化搜索,就是对于会重复遍历或者说重复计算的部分,直接用数组或者其他集合去存起来,这样当下一次如果计算到相同的值的时候,就可以免去计算直接拿出这个想要的值了。

//从后向前遍历做法
class Solution {
public:
	int help(vector<int>nums,int index,vector<int>&vis){
		if(index==0)return nums[0];
		if(index==1)return max(nums[0],nums[1]);
		if(vis[index]!=-1)return vis[index];//如果不是-1,说明递归计算过了
		int use=help(nums,index-2,vis)+nums[index];//选了当前这个数
		int ofuse=help(nums,index-1,vis);//不选当前这个数
		int m=max(use,ofuse);
		vis[index]=m;//记忆化存储
		return m;
}
    int rob(vector<int>& nums) {
    vector<int>ans(nums.size(),-1);//初始值为-1,如果递归节点的值已经 
    if(nums.size()==0)return 0;
    return help(nums,nums.size()-1,ans);
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

DP

自底向上。将大规模的问题转换为小规模的问题。基本上所有的动态规划都可以使用递归的式子来转换过来。
要素:

  1. 初始化:可以根据状态方程式来设置初始值。
  2. 状态转移方程式:想一下递归式子,要根据题意推导。
  3. 缓存中间结果:用数组或者其他集合来缓存。一般数组大小为原数组大小。
  4. 顺序问题:要找到依赖关系,当前的对象由谁推导出来的。
class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.size()==0)return 0;
        if(nums.size()==1)return nums[0];
        int dp[110];
        dp[0]=nums[0];
        dp[1]=max(nums[0],nums[1]);
        for(int i=2;i<nums.size();i++){
            dp[i]=max(dp[i-1],dp[i-2]+nums[i]);//可以状态压缩
        }
        return dp[nums.size()-1];
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

动态规划详解

介绍

拿到一个题目,如何知道这个题目能用动态规划?

状态是什么要如何判断?如何准确定义?

基本题型
线性DP
子序列和子串
首先是几个需要熟记的点:

  1. 子序列:子序列并不要求连续
  2. 子串:子串一定是原始字符串的连续子串
  3. 回文串:一个字符串,正着读和反着读是一样的。

入门题目:

  1. 数字三角形
  2. 滑雪

经典题目

  1. 不同路径II
  2. 最大子序和
  3. 最长上升子序列
  4. 最长公共子序列
  5. 最长重复子数组
  6. 最长回文子串

背包

0-1背包问题:
问题描述:现在有N件物品,一个背包容量为M,每个物品的体积为v,每个物品的价值是w,现在要求出这N件物品的最大价值
状态方程分析:像这种先确定是使用一维还是二维,首先物品肯定要算上,另外还有限制条件就是物体的体积,同时考虑问题要求出什么,现在是求出这总共的N件物品的最大价值,可以先考虑第一件物品,第一和第二…同时标注出前i件物品使用多少体积,因此得到dp定义:dp[i][j]:前i件物品在使用体积不超过j的情况下的最大价值
代码实现:

#include<iostream>
using namespace std;
const int N = 1010;
int n, m;//n物品个数,m是背包的容量
int f[N][N];//最大价值
int v[N], w[N];//V是每个物品的体积,w是每个物品的价值
int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> v[i] >> w[i];
	}
	//初始化就是0件物品最大价值肯定是0
	for (int i = 1; i <= n; i++) {
		for (int j = 0; j <= m; j++) {
		//如果不取出这第i件物品,那体积无变化
			f[i][j] = f[i - 1][j];
			//如果取出这第i件数物品放入背包,首先要保证前i件物品使用的体积大于这第i件物品的体积,不然根本放不下去。
			if (j>=v[i]) {
			//拿出第i件物品放入背包,那现在的就可以正常的由前面一件物品推出来,
			//同时,同一个i要找出满足最大价值的j,遍历物品结束之后找到n件物品在背包容量m的约束下最大的价值
				f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
			}
		}
	}
	int res = 0;
	//已经算出了不同的i最合适的j了,直接固定n在0-m里面找最合适的i就行了,其实这里直接写f[n][m]就是可以的
	for (int i = 0; i <= m; i++) {
	    res=max(res,f[n][i]);
	}
	cout << res << endl;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

状态压缩

#include<iostream>
using namespace std;
const int N = 1010;
int n, m;//n物品个数,m是背包的容量
int f[N];//最大价值
int v[N], w[N];//V是每个物品的体积,w是每个物品的价值
int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> v[i] >> w[i];
	}
	//初始化就是0件物品最大价值肯定是0
	for (int i = 1; i <= n; i++) {//物品个数
		for (int j = m; j >= v[i]; j--) {//背包体积变化
				f[j] = max(f[j], f[j - v[i]] + w[i]);
			}
		}
	}
	//已经算出了不同的i最合适的j了,直接固定n在0-m里面找最合适的i就行了,其实这里直接写f[n]就是可以的
	cout << f[m]<< endl;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

遍历顺序:关于内循环遍历背包的容量是从后向前遍历的原因是:如果从前向后遍历,那更新的dp值就会覆盖以前的dp值,当下一步要利用以前的状态去推导的时候就会发生错误,而从后向前更新,不会覆盖以前的dp值。
经典题目:

  1. List item

完全背包问题:

打家劫舍
股票

  1. List item

待补:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/87262
推荐阅读
相关标签
  

闽ICP备14008679号