算法总结-DP入门

概览

一个问题必须有重叠子问题最优子结构(这个问题的最优解可以由其子问题的最优解有效的构造出来)才可以用动态规划解决。

解决方式

  1. 递推:自底向上
  2. 递归自顶向下

贪心与动态规划的区别
贪心是局部最优解,在每一次选择中,选了就不后悔。动态规划则考虑所有的子问题,选择能笑到最后的。

1) 确定递推量。 这一步需要确定递推过程中要保留的历史信息数量和具体含义, 同时也会定下动态规划的维度;
2) 推导递推式。 根据确定的递推量, 得到如何利用存储的历史信息在有效时间(通常是常量或者线性时间)内得到当前的信息结果;
3) 计算初始条件。 有了递推式之后, 我们只需要计算初始条件, 就可以根据递推式得到我们想要的结果了。 通常初始条件都是比较简单的情况, 一般来说直接赋值即可;
4) (可选)考虑存储历史信息的空间维度。 这一步是基于对算法优化的考虑, 一般来说几维动态规划我们就用几维的存储空间是肯定可以实现的。 但是有时我们对于历史信息的要求不高, 比如这一步只需要用到上一步的历史信息, 而不需要更早的了, 那么我们可以只存储每一步的历史信息, 每步覆盖上一步的信息, 这样便可以少一维的存储空间, 从而优化算法的空间复杂度。 动态规划的时间复杂度是O((维度)×(每步获取当前值所用的时间复杂度))。 基本上按照上面的思路, 动态规划的题目都可以解决, 不过最难的一般是在确定递推量, 一个好的递推量可以使得动态规划的时间复杂度尽量低。

数塔问题

//数塔问题,将一些数字排成塔状,第n层有n个数字,现在从第一层走到第n层
//最后路径上最大的数字之和为多少
//用DFS做复杂度O(2^n),存在大量的重复子问题
//状态转移方程:dp[i][j] = max(dp[i+1][j],dp[i+1][j+1])+f[i][j]
#include<cstdio>
#include<algorithm>
using namespace std;

int f[1000][1000] = {0};
int dp[1000][1000] = {0};

int main(){
	int n;
	scanf("%d",&n);
	for(int i = 1;i <= n;i++){
		for(int j = 1; j <= i;j++){
			scanf("%d",&f[i][j]);
		}
	}
	
	for(int j = 1;j <= n;j++){
		dp[n][j] = f[n][j];
	}
	
	for(int i = n-1;i >=1;i--){
		for(int j = 1;j <= i;j++){
			dp[i][j] = max(dp[i+1][j],dp[i+1][j+1])+f[i][j];
		}
	}
	
	printf("%d",dp[1][1]);
} 

最大连续子列和

//最大连续子序列和
//dp[i]表示以i结尾的数列的最大值
//状态转移方程 dp[i] = max(arr[i],dp[i-1]+arr[i]) 
#include<cstdio>
#include<algorithm>
using namespace std;


int main(){
	int arr[] = {-2,11,-4,13,-5,-2};
	int dp[6];
	dp[0] = arr[0];
	
	for(int i = 1;i < 6;i++){
		dp[i] = max(arr[i],dp[i-1]+arr[i]);
	}
	
	int maxSum = -1;
	for(int i = 0;i < 6;i++){
		if(dp[i] > maxSum){
			maxSum = dp[i];
		}
	}
	
	printf("%d",maxSum);
} 

最长不下降子序列

//最长不下降子序列
//状态转移方程dp[i] = max(1,dp[j]+1),条件:arr[j] <= arr[i] && dp[j]+1>dp[i] 
//时间复杂度O(n^2) 

#include<cstdio>
#include<algorithm>
using namespace std;
int main(){
	int n;
	scanf("%d",&n);
	int arr[1000],dp[1000];
	for(int i = 0;i < n;i++){
		scanf("%d",&arr[i]);
	}
	
	int ans = -1;
	for(int i = 0;i < n;i++){
		dp[i] = 1;
		for(int j = 0;j < i;j++){
			if(arr[j] <= arr[i] && dp[j]+1>dp[i]){
				dp[i] = dp[j]+1;
			}
		}
		ans = max(ans,dp[i]);
		
	} 
	
	printf("%d",ans);
} 

最长公共子序列

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;

//最长公共子序列
//d[i][j]表示A的i号位和B的j号位之前的最长公共子序列
//d[i][j] = dp[i-1][j-1]+1;A[i] = b[i]
//d[i][j] = max(dp[i-1][j],dp[i][j-1]);A[i] != b[i]
int main() {

    int dp[100][100];

    string A = " sadstory";
    string B  = " adminsorry";

    int lenA = A.size()-1;
    int lenB = B.size()-1;

    for(int i = 0;i <= lenA;i++){
        dp[i][0] = 0;
    }

    for(int i = 0;i <= lenB;i++){
        dp[0][i] = 0;
    }

    for(int i = 1;i <= lenA;i++){
        for(int j = 1;j <= lenB;j++){
            if(A[i] == B[j]){
                dp[i][j] = dp[i-1][j-1]+1;
            }else{
                dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
            }
        }
    }

    printf("%d",dp[lenA][lenB]);



}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值