算法---动态规划 I

本文深入探讨了动态规划的概念、特点和本质,强调其在解决最大最小值、可行性及方案计数等问题上的应用。通过斐波那切数列、变态青蛙跳台阶、矩形覆盖和连续子数组最大和等经典案例,详细阐述了动态规划的解题步骤,包括状态定义、状态转移方程、初始化和返回结果。此外,还涉及了字符串分割问题,展示了动态规划在不同场景中的灵活运用。
摘要由CSDN通过智能技术生成

一、动态规划问题的一般解决过程

1、动归介绍

1)动归的定义

动态规划是分治思想的延伸,通俗来说就是”大事化小,小事化无“。

再将大问题化解为小问题的分治过程中,保存这些小问题已经处理好的结果供后面处理更大问题时使用这些结果。

2)动归的特点

  • 把原来的问题分解成了几个相似的子问题
  • 所有的子问题都只需要解决一次
  • 存储子问题的解

3)动态规划的本质

对问题状态的定义以及状态转移方程的定义(状态以及状态之间的递推关系)。

2、动态规划解题过程

动态规划问题一般从以下四个角度考虑:

  • 状态定义
  • 状态之间的转移方程
  • 状态的初始化
  • 返回结果

状态定义的要求:定义的状态一定要形成地推关系。

3、适合用动态规划解决的问题

  • 最大最小值问题
  • 是否可行问题
  • 方案个数

二、动态规划的题目

1、菲波那切数列

1)问题分析

斐波那切数列是一个非常典型的动态规划题目,在斐波那切数列中除第0、1项外其余项都是前两项的和。求解斐波那切数列第n项和最多的解法就是递归,如下:
递归思路:每一项都是前两项的和,求第i项求出第i-1和第i-2项即可。当i = 0或i = 1时,直接返回0。可以写出如下代码

int fib(int n)
{
    if(n <= 1)
        return n;
    return fib(n-1)+fib(n-2);
}

对上述代码进行分析:

动态规划思想:将大问题分解成小问题,保存小问题的解,在计算大问题时使用小问题的解。

解题思路:

  • 状态定义:斐波那切数列的第i项f(i)。
  • 状态之间的转换方程:f(i) = f(i-1) + f(i-2) (题目已知条件条件)
  • 状态的初始结果:f(0) = 0,f(1) = 1; (题目已知条件)
  • 返回结果:f(i)

2)代码描述

class Solution {
public:
    int Fibonacci(int n) {
        if(n <= 1)
            return n;
        int fn1 = 1;//f(n-1)初始状态为f(1) = 1
        int fn2 = 0;//f(n-2)初始状态f(0) = 0
        int fn;//返回结果
        for(int i = 2;i <= n;i++)
        {
            fn = fn1+fn2;//状态转移方程f(n) = f(n-1) + f(n-2)
            fn2 = fn1;//状态改变
            fn1 = fn;
        }
        return fn;
    }
};

2、变态青蛙跳台阶

1)问题分析

  • 状态定义:青蛙跳上i级台阶的所有跳法f(i)
  • 状态之间的转移方程:f(i) = 2*f(i-1)

  • 初始状态:f(1) = 1
  • 返回结果:f(i)

2)代码描述

class Solution {
public:
    int jumpFloorII(int number) {
        //写法1
        /*if(number <= 1)
            return number;
        int fn1 = 1;//初始状态f(1)=1
        int fn;//结果
        for(int i = 2;i <= number;i++)
        {
            fn = 2*fn1;
            fn1 = fn;
        }
        return fn;*/
        
        //写法2
        //每一项的值都是前一项的2倍,第一项的值为1,等比数列:sn = 2^n - 1
        return (1<<number)/2;
    }
};

3、矩形覆盖

1)问题分析

  • 状态定义:i个小矩形覆盖成一个2*i的大矩形的方法f(i)
  • 状态之间转移方程:f(i) = f(i-1) + f(i-2)

  • 初始状态:f(1) = 1,f(2) = 2
  • 返回结果:f(i)

2)代码描述

class Solution {
public:
    int rectCover(int n) {
        if(n <= 2)
            return n;
        int fn1 = 2;//f(n-1)初始状态为f(2) = 2
        int fn2 = 1;//f(n-2)初始状态f(1) = 1
        int fn;//返回结果
        for(int i = 3;i <= n;i++)
        {
            fn = fn1+fn2;//状态转移方程f(n) = f(n-1) + f(n-2)
            fn2 = fn1;//状态改变
            fn1 = fn;
        }
        return fn;
    }
};

4、连续子数组最大和

1)问题分析

  • 状态定义: 以第i个元素结尾的最大连续子序列和f(i)
  • 状态之间的转移方程:f(i) = max({f(i-1)+a[i]},a[i])

  • 初始状态:f(1) = a[0]
  • 返回结果:max(f1,f2,...,f(i))

2)代码描述

class Solution {
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
        //写法1---保存所有以i结尾的连续子序列的最大和
        /*vector<int> maxArray;
        maxArray.push_back(array[0]);//初始状态
        for(int i = 1;i < array.size();i++)
        {
            if(maxArray[i-1]+array[i] < array[i])
                maxArray.push_back(array[i]);
            else
                maxArray.push_back(maxArray[i-1]+array[i]);
        }
        //选择最大的返回
        int max = maxArray[0];
        for(int i = 1;i < maxArray.size();i++)
        {
            if(maxArray[i] > max)
                max = maxArray[i];
        }
        return max;*/
        
        //写法2---只保存f(i-1)
        int fi1 = array[0];//初始状态
        int max = array[0];//返回结果
        for(int i = 1;i < array.size();i++)
        {
            if(fi1+array[i] > array[i])
                fi1 += array[i];
            else
                fi1 = array[i];
            //更新max
            if(max < fi1)
                max = fi1;
        }
        return max;
    }
};

5、分割字符串

1)问题分析

2)代码描述

class Solution {
public:
    bool wordBreak(string s, unordered_set<string> &dict) {
        int len = s.size();
        vector<int> status(len+1,0);//保存状态
        status[0] = 1;//初始状态f(0) = 1
        for(int i = 1;i <= len;i++)
        {
            for(int j = i-1;j >= 0;j--)
            {
                if(status[j] && dict.find(s.substr(j,i-j)) != dict.end())
                    status[i] = 1;//状态之间的转换
            }
        }
        return status[len];
    }
};

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疯狂嘚程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值