动态规划(DP)----几个例子--太惨了吧

本文介绍了如何将递归转换为动态规划,通过动态规划解题的四个步骤,包括确定子问题、状态、初始状态和状态转移方程。文中列举了数字三角形问题和最长上升子序列作为示例,展示了动态规划在解决这类问题中的应用。此外,还探讨了动态规划的递归型和递推型两种形式及其效率差异。
摘要由CSDN通过智能技术生成

递归到动归的一般转换

递归函数有n个参数,就定义一个n维数组,数组的下标是参数的取值范围,数组元素

的值是递归函数的返回值,这样就可以从边界开始,逐步填充数组

动规的解题步骤

1,把原问题分解为子问题,子问题的解一旦求出就会被保存,所以每个子问题只需要求一次

2,确定状态,一个状态值就是一个或多个子问题的解

3,确定一些初始状态(边界状态的值),我们可以简单地额计算或者一眼就可以看出来的边界状态值

4,状态转移方程:即如何从一个或多个“值”已知的“状态”,

求出另一个状态的值,即“人人为我”递推型。

(问题具有最优子结构性质,无后效性的问题才能用动规)

动态规划常用的两种形式

1,递归型:直观,容易编写,可以使用滚动数组(比如说,第一步求出的值存在一个数组中,第二步求出的值后已经不需要第一步求出的了,可以直接覆盖之前的,还是存在同一个数组内)

2,递推型,效率高,可以避免函数调用,快一些,但是我觉得这样看的不清楚qwq

 

 

 

 

1,数字三角形问题

7

3 8

8 1 0

2 7 4 4

4 5 2 6 5

在上面的数字三角形中寻找一条从顶部到底边的路径,使得路径上所有经过的数字之和最大。

路径上的每一步都只能往左下或者右下走。只需要求出这个最大和即可,不必给出具体路径。

三角形行数大于1小于等于100,数字为0-99.

#include <iostream>
#include<math.h>
#include<algorithm>
using namespace std;

//记忆型递归型动态规划解决数字三角形问题
#define MAX 101
int D[MAX][MAX];           //保存数字三角形里的值 
int maxsum[MAX][MAX];     //初始化为全部为-1,保存i,j位置上的最大和 
int n;                     //数字三角形的行数 
int MaxSum(int i, int j)
{
	if(maxsum[i][j] != -1)
		return maxsum[i][j];
	if(i == n)
		maxsum[i][j] = D[i][j];
	else
	{
		maxsum[i][j] = max(maxsum[i+1][j], maxsum[i+1][j+1]) + D[i][j];
	}
 }

int main()
{
	cin >> n;
	int i, j;
	for(i = 1; i <= n; i++)
		for(j = 0; j <= i; j++)
		{
			maxsum[i][j] = -1;
			cin >> D[i][j];
		}
	cout << MaxSum(1,1) << endl;
 } 

 

还是很好理解的对吧。

 

 

还可以用循环的方式实现递推

//用循环实现递推,值需要一个双重循环就够了 

int main() 
{
	cin >> n;
	int i, j;
	for(i = 1; i <=n ;i++)
		for(j = 1; j <= i ; j++)
			{
				cin >> D[i][j];
			}
	
	//先初始化最底下一层 
	for(j = 1; j <= n; j++)
		maxsum[n][j] = D[n][j];
		
	//从下往上的过程 
	for(i = n-1; i >= 1; i--)
		for(j = 1; j <= i; j++)
		{
			maxsum[i][j] = max(maxsum[i+1][j], maxsum[i+1][j+1]) + D[i][j];
		}
	cout << maxsum[1][1] << endl;
}

 

2,最长上升子序列(题目自行百度),这个题目的子问题是以ak为结尾的最长子序列的长度,存放在maxlen中

#define MAX 100 
int D[MAX];
int maxlen[MAX];
int n;

//以ax为结尾的最长的子序列的长度 

int main()
{
    cin >> n;
    int i, j;
    for(i = 0; i < n; i++)
    {
        cin >> D[i];
        maxlen[i] = 1;
     } 
     
     int ans = 0;
    for(i = 1; i < n; i++)
    {
        for(j = 0; j < i; j++)
        {
            if(D[i] > D[j])                             //这一步我真的是蠢到爆炸,写成了maxlen[i] > maxlen[j]
                maxlen[i] = max(maxlen[i], maxlen[j] + 1);    
        }
        ans = max(ans, maxlen[i]);
    }
    cout << ans << endl;
 } 

 

3,给出两个字符串,求出这样的一个最长公共子序列的长度:

子序列中的每一个字符都能在原来的串中找到,而且每个字符的先后顺序和原串中的先后顺序一致。

//最长公共子序列
//动态规划的题目还是要具体问题具体分析,但是还是有一定的套路可寻的 
#define MAX 100
int dp[MAX][MAX];       //dp[i][j]存放s1左边i个字母和s2左边j个字母的最长公共子列的长度 
char s1[MAX];
char s2[MAX];
int main()
{
	int n;
	cin >> n;
	int i, j;
	for(i = 0; i < n; i++)
	{
		cin >> s1[i];
	}
	
	for(i = 0; i < n; i++)
	{
		cin >> s2[i];
	}
	
	//确定边界
	//dp[i][0]和dp[0][i]都是等于0
	 
	 for(i = 0; i < n; i++)
	{ 	
		dp[0][i] = 0;
		dp[i][0] = 0;
	}
	
	//dp可以想象成一个横竖的表,第一行和第一列已经是作为边界已知的为0了 
	//然后接下来就是要用已知的边界值去填充未知的dp 
	//按照状态转换方程来填充啦~ 
	for(i = 1; i <= n; i++)
	{
		for(j = 1; j <= n; j++)
		{
			if(s1[i] == s2[j])
				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][n] << endl;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值