动态规划
- 确定递推状态: f(n)+解释
- 确定递推公式
- 程序实现
优化:
- 去除冗余状态
- 状态重定义
- 优化转移过程
- 斜率优化
优化-递归+记忆化
if arr[n] return arr[n]递归+记忆化(正向求解-递归) 或 改变求解顺序(逆向递归求解-循环) : 解决效率问题
用long long ,或者更大的:解决数值溢出,不够大问题
HZOJ 38 兔子繁殖问题
- 确定递推状态: f(n)为第n个月的时候兔子的数量
- 确定递推公式: f(1)=1 | f(2)=2 | f(n)=f(n-1)+f(n-2)
- 程序实现
#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))
容斥原理的基本思想
- 在计数的时候为了没有重复 没有遗漏
- 再不考虑重叠的情况,把包含于某内容的所有对象数目先计算出来,然后把计数时重复的计算的数目排斥出去
加多的减掉,减多了加回来
- 结果既无遗漏又无重复—容斥原理
钱币问题
20240325222843856.png&pos_id=img-HkM4gDV3-1711514212835)
- 确定递推状态:
f[i][j]表示用i种钱币,凑足j元钱的方法总数1 2 5 f(2)(5) ,前两种钱币凑5块有
1 2 21 1 1 21 1 1 1 1三种
- 确定递推公式
拆分:分成两个不相交的子集
a. 没有使用第i种钱币
b. 使用了第i种钱币
f[i][j]=f[i-1][j]+f[i][j-w[i]]w:1 2 5
f(3)(5)=f(3-1)(5)+f(3)(5-5)
=f(2)(5)+f(3)(0)
=f(1)(5)+f(2)(3)+f(3)(0)
=f(1)(5)+f(1)(3)+f(2)(1)+f(3)(0)
=f(1)(5)+f(1)(3)+f(1)(1)+f(2)(0)+f(3)(0)
=f(0)(5)+f(0)(3)+f(0)(1)+f(1)(0)+f(2)(0)+f(3)(0)
=0+0+0+0+1+1+1
=3
- 程序实现
//
// Created by cry on 2024/3/27.
//
#include <iostream>
using namespace std;
#define MAX_N 10000
#define MAX_M 20
int w[MAX_N+5];
int f[MAX_M+5][MAX_N+5]; //前m种钱币拼凑n元钱
void acm_1_14_dp_money_test(){
int m,n;
cin >> m>> n;
for(int i=1;i<=m;i++) { //钱币类型遍历
cin >> w[i];
}
for(int i=1;i<=m;i++){
f[i][0]=1; //前i种钱币凑0元钱为1种
// 因为f(2)(5)+f(3)(0)=f(3)(5)
// 3+1=4,所以f(3)(0)为1
for (int j = 1; j <= n; j++) {
f[i][j]=f[i-1][j];
if(j<w[i])continue; //小于第i种钱币的面额
f[i][j]+=f[i][j-w[i]];
f[i][j] %= 9973;
}
}
cout << f[m][n] << endl;
}
使用python实现
def get_dp_dp_money(m,n,w):
f=[[0]*(n+5)]*(m+5)
for i in range(1,m+1):
f[i][0]=1 # 终止条件
for j in range(1,n+1):
f[i][j]=f[i-1][j]
if(j<w[i-1]):continue #f(1)(3)没有值,为0
f[i][j]+=f[i][j-w[i-1]]
f[i][j]%=9973
print(f[m][n])
if __name__=='__main__':
m,n=list(map(int,input().split(" ")))
w=list(map(int,input().split(" ")))
w.append(0)
get_dp_dp_money(m,n,w)
if(j<w[i-1]):continue #f(1)(3)没有值,为0
f[i][j]+=f[i][j-w[i-1]]
f[i][j]%=9973
print(f[m][n])
if name==‘main’:
m,n=list(map(int,input().split(" “)))
w=list(map(int,input().split(” ")))
w.append(0)
get_dp_dp_money(m,n,w)
本文详细介绍了动态规划中的递推状态确定、记忆化技巧的应用以及优化方法,包括去除冗余状态、斜率优化和使用大整数类型处理数值溢出。通过实例如兔子繁殖问题和钱币问题,展示了如何使用递归和记忆化求解,并讨论了容斥原理在计数问题中的应用。
248

被折叠的 条评论
为什么被折叠?



