第14章动态规划

动态规划

  • 确定递推状态: f(n)+解释
  • 确定递推公式
  • 程序实现

优化:

  • 去除冗余状态
  • 状态重定义
  • 优化转移过程
  • 斜率优化

优化-递归+记忆化

if arr[n] return arr[n]

递归+记忆化(正向求解-递归) 或 改变求解顺序(逆向递归求解-循环) : 解决效率问题

用long long ,或者更大的:解决数值溢出,不够大问题

HZOJ 38 兔子繁殖问题

  1. 确定递推状态: f(n)为第n个月的时候兔子的数量
  2. 确定递推公式: f(1)=1 | f(2)=2 | f(n)=f(n-1)+f(n-2)
  3. 程序实现
#include <iostream>
using namespace std;
#define MAX_N 100
int digui_jiyihua_arr[MAX_N+5]={
   
   0};

int f(int n){
   
   
    if(n<=2) return n;
    if(digui_jiyihua_arr[n])return digui_jiyihua_arr[n]; //优化-增加记忆点,解决超时问题
    digui_jiyihua_arr[n]=f(n-1)+f(n-2);
    return digui_jiyihua_arr[n];
}
void acm_1_14_digui_jiyihua_test(){
   
   
    int n;
    cin>>n;
    cout << f(n) <<endl;
}

改变求解顺序

从低到高

long long acm_1_14_digui_jiyihua_f[105]={
   
   0};
void acm_1_14_digui_jiyihua_test2(){
   
   
    int n;
    cin>>n;
    acm_1_14_digui_jiyihua_f[1]=1;
    acm_1_14_digui_jiyihua_f[2]=2;
    for(int i=3;i<=n;i++){
   
   
        acm_1_14_digui_jiyihua_f[i]=acm_1_14_digui_jiyihua_f[i-1]+acm_1_14_digui_jiyihua_f[i-2];
    }
    cout << acm_1_14_digui_jiyihua_f[n] <<endl;
}
//
// Created by cry on 2024/3/24.
//
#include <vector>
#include <iostream>
using namespace std;
#define MAX_N 100
long long digui_jiyihua_arr[MAX_N+5]={
   
   0};

long long f(int n){
   
   
    if(n<=2) return n;
    if(digui_jiyihua_arr[n])return digui_jiyihua_arr[n]; //优化-增加记忆点,解决超时问题
    digui_jiyihua_arr[n]=f(n-1)+f(n-2);
    return digui_jiyihua_arr[n];
}
void acm_1_14_digui_jiyihua_test(){
   
   
    int n;
    cin>>n;
    cout << f(n) <<endl;
}
//大整数
class BigInt:
public vector<int>{
   
   
        public:
        BigInt()
        {
   
   
            push_back(0);
        }
        BigInt(int x)
        {
   
   
            this->push_back(x);
            process_digit();//处理大整数进位问题
        }
        //重载+=运算
        BigInt &operator+=(const BigInt &a){
   
   
            for(int i=0;i<a.size();i++){
   
   
                if(i >= size()) //如果位数超出了
                {
   
   
                    push_back(a[i]);

                }else at(i)+=a[i];
            }
            process_digit();
            return *this;
        }
        //重载加法运算
        BigInt operator+(const BigInt &a){
   
   
            BigInt ret(*this);//拷贝一下当前
            ret+=a;
            return ret;
        }
        //重载左移运算符

        //进位
        void process_digit() {
   
   
            for (int i = 0; i < size(); i++) {
   
   
                if (at(i) < 10) continue; //当前位置的值小于10,不需要处理
                //当前位置的值大于10,就进位
                if (i == size() - 1) push_back(0); //增加一位
                at(i + 1) += at(i) / 10;
                at(i) %= 10;
            }
            return ;
        }
};
// 重写左移运算符
ostream &operator<<(ostream &out,const BigInt &a){
   
   
    for(int i=a.size()-1;i>=0;i--){
   
   
        out << a[i];
    }
    return out;
}
BigInt acm_1_14_digui_jiyihua_f[105]={
   
   0};
void acm_1_14_digui_jiyihua_test2(){
   
   
    int n;
    cin>>n;
    acm_1_14_digui_jiyihua_f[1]=1;
    acm_1_14_digui_jiyihua_f[2]=2;
    for(int i=3;i<=n;i++){
   
   
        acm_1_14_digui_jiyihua_f[i]=acm_1_14_digui_jiyihua_f[i-1]+acm_1_14_digui_jiyihua_f[i-2];
    }
    cout << acm_1_14_digui_jiyihua_f[n] <<endl;
}
int main() {
   
   
    acm_1_14_digui_jiyihua_test2();
    return 0;
}

python解法 只需要记忆化就行了,类型不会出问题

#第一代,缺点:效率低 且 数据类型不够大
# dp_1(65) 直接跑不完
def dp_1(n):
    if(n<=2):
        return n
    return dp_1(n-1)+dp_1(n-2)
# 解决:添加记忆化
MAX_N=100
arr=[0]*(MAX_N+5)
def dp_2(n):
    if(n<=2):
        return n
    if arr[n]:return arr[n]
    arr[n]=dp_2(n-1)+dp_2(n-2)
    return arr[n]
n=int(input())
print(dp_2(n))
# 改变求解顺序
def dp_3(n):
    arr=[0]*(MAX_N+5)
    if(n<=2):return n
    arr[1]=1
    arr[2]=2
    for i in range(3,n+1):
        arr[i]=arr[i-1]+arr[i-2]
    return arr[n]
print(dp_3(n))

容斥原理的基本思想

  1. 在计数的时候为了没有重复 没有遗漏
  2. 再不考虑重叠的情况,把包含于某内容的所有对象数目先计算出来,然后把计数时重复的计算的数目排斥出去

加多的减掉,减多了加回来

  1. 结果既无遗漏又无重复—容斥原理

钱币问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

厨 神

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

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

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

打赏作者

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

抵扣说明:

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

余额充值