介绍
是用来解决一类最优问题的算法思想,将一个复杂的问题分解成若干个子问题,通过综合子问题的最优解得到。
递归写法实例
优化斐波那契数列
int F(int n){
if(n==0||n==1) return 1;
else{
return F(n-1)+F(n-2);
}
有太多重复计算,可用一个数组记录计算的结果,出现过则直接返回,无需再进行重复计算
const int maxn=100;
int dp[maxn];
int F(int n){
if(n==0||n==1) return 1;
if(dp[n]!=-1) return dp[n];
else{
dp[n]=F(n-1)+F(n-2);
return dp[n];
}
递推写法实例
求数塔问题
思路:由底往上推求和
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=100;
int str[maxn][maxn],num;
int dp[maxn][maxn];
int main(){
cin>>num;
//输入
for(int i=1;i<=num;i++){
for(int j=1;j<=i;j++){
cin>>str[i][j];
}
}
//由底往上开始,初始化边界的状态
for(int j=1;j<=num;j++){
dp[num][j]=str[num][j];
}
//计算
for(int i=num-1;i>0;i--){
for(int j=1;j<=i;j++){
dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+str[i][j];
}
}
cout<<dp[1][1]<<endl;
return 0;
}
典型例题
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=100;
int str[maxn],num;
int dp[maxn];
int main(){
cin>>num;
for(int i=0;i<num;i++){
cin>>str[i];
}
//求和
dp[0]=str[0];//从0开始记录最大和
for(int i=1;i<num;i++){
//当i-1之前的结点为负数时,最大值为本身,否则求和最大
dp[i]=max(str[i],dp[i-1]+str[i]);
}
//求下标
int k=0;
for(int j=1;j<num;j++){//无需遍历0下标
if(dp[k]<dp[j])
k=j;
}
cout<<dp[k]<<endl;
return 0;
}
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=100;
int str[maxn],num;
int dp[maxn];
int main(){
cin>>num;
for(int i=0;i<num;i++){
cin>>str[i];
}
int k=-1;//找最长子序列
for(int i=0;i<num;i++){
dp[i]=1;//先初始化为最短状态:即本身一个序列
for(int j=0;j<i;j++){
//i>j,首先是递增,正常最小:dp[i]=dp[j]+1,说明后一个小于前一个
if(str[i]>=str[j]&&dp[j]+1>dp[i])
dp[i]=dp[j]+1;
}
k=max(k,dp[i]);
}
cout<<k<<endl;
return 0;
}
砝码称重
思路:
记:左-负 右-正 不选-0
计算总质量-w[i]
利用递推去实现:定义一个动态数组dp[i][j]-表示从前i个物品中选重量为j的砝码,
则i:由1-N;j:由0-总重量 dp=记录种类数
不同情况:1.不选:dp[i][j]=f[i-1][j]-重量不变
2.放左边 :dp[i][j]=f[i-1][j-w[i]]-放左边重量-:对负数处理-abs(负权)
3.放右边:dp[i][j]=f[i-1][j+w[i]]-放右边重量+
则其转移方程:dp[i][j]=f[i-1][j+w[i]]||dp[i][j]=f[i-1][j-w[i]]||dp[i][j]=f[i-1][j]
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
const int maxn=110;
int n,num=0,w[maxn],dp[maxn][1e5];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
int m=0;
//输入求和
for(int i=1;i<=n;++i) {
cin>>a[i];
m+=a[i];
}
//启示录
f[0][0]=1;//重量为0的砝码,记录
//
for(int i=1;i<=n;++i)//个数
for(int j=m;j>=0;--j)//j:总重量->0变化
f[i][j]=f[i-1][j]||f[i-1][abs(j-a[i])]||f[i-1][j+a[i]];
int ans=0;
for(int i=1;i<=m;++i)//遍历不同质量的砝码,统计种类
if(f[n][i]) ans++;
cout<<ans<<endl;
return 0;
}
接龙序列
//先声明这不是我写的,这是大佬的代码!!!!!
//因为我差不多花了1小时才理解,尊滴很无助,所以想分享一下自己的理解,让大家能有点借鉴吧,
#include <iostream>
#include <bits/stdc++.h>
#include <string>
using namespace std;
const int maxn=10;
int n,dp[maxn];
int t,h,m=0;
string s;
//求最少删除的个数——>求最长的接龙序列长度——>用动态数组记录最大数m——>用总数-m为所求值
//其中动态数组:下标记录字符,值记录出现次数
int main()
{
cin>>n;
for(int i=0;i<n;i++){
cin>>s;
h=s[0]-'0';
t=s[s.length()-1]-'0';
dp[t]=max(dp[h]+1,dp[t]);//可实现首尾字符出现次数++,因为dp[h]+1,而dp[t]=dp[h]+1,相当于次数++;记录两者最大值可得最长接龙长度
m=max(m,dp[t]);//记录下来
}
cout<<n-m<<endl;
return 0;
}
叠硬币
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=100;
int num,cou=0;
int n,m;
int dp[maxn];
int re(){
dp[0]=dp[1]=1;
for(int i=2;i<=n;i++){
dp[i]=dp[i-1]+dp[i-2];
if(dp[i]%m==0)
cou++;
}
return cou;
}
int main()
{
memset(dp,0,sizeof(dp));
cin>>num;
// for(int i=0;i<num;i++){
cin>>n>>m;
cout<<re();
// }
// 请在此输入您的代码
return 0;
}
2022
#include <iostream>
using namespace std;
int main()
{
// 2022->10个不同的
2022=2*1000+20+2=1011*2
return 0;
}
#include <bits/stdc++.h>
using namespace std;
long long pd[2022+1][10+1]{1};
int main() {
for (int i = 1; i <= 2022; i++)
for (int j = 1; j <= 10; j++)
if (i >= j) pd[i][j] = pd[i - j][j] + pd[i - j][j - 1];
cout<<pd[2022][10];
}
最小权值
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll num[2030];
int main()
{
// ios::sycn_with_stdio(0);
// cin.tie(0);cout.tie(0);
memset(num,127,sizeof(num));
num[0]=0;
for(int i=1;i<=2021;++i){
for(int j=0;j<i;++j){
num[i]=min(num[i],1+2*num[j]+3*num[i-j-1]+j*j*(i-j-1));
}
}
cout<<num[2021];
// 请在此输入您的代码
return 0;
}
小蓝与钥匙
#include <iostream>
using namespace std;
typedef long long ll;
ll f[2030];
ll c(int n,int m){
ll res=1;
for(int i=1;i<=m;++i){
res=res*(n-i+1)/i;
}
return res;
}
int main()
{
f[1]=0,f[2]=1;
for(int i=3;i<=14;++i){
f[i]=(i-1)*(f[i-1]+f[i-2]);
}
cout<<c(28,14)*f[14];
return 0;
}