学习笔记-数据结构与算法 dp模型 (动态规划)

动态规划

ps:最优子结构,无后效性,子问题的重叠性>记忆化搜索

斐波纳契列模型
  • 开数组存结果
  • 计算之前先看是否计算过了,如果算过了直接返回结果
#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
int dp[maxn];
// 记忆化搜索的基本结构
// 要怎么表示没被算过?
int f (int x)
{
    // 算过了就不算了, 记忆化搜索的关键,一定要放在dfs的开头!!!!
    if (dp[x] != -1) return dp[x];
    if (x == 1 || x == 2) return 1;
    int ans = f(x - 1) + f(x - 2);
    dp[x] = ans;
    return ans;
}
int main()
{
    int n; cin >> n;
    // 把所有dp置为-1
    // memset 不是什么值都可以赋的!
    // 只能初始化0 || -1
    memset(dp , -1 , sizeof dp);
    cout << f(n) << endl;
    return 0;
}

数塔问题模型
1.记忆化搜索
//数塔问题
#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
const int inf = 1e9;
int dp[maxn][maxn] , a[maxn][maxn];
int n , m;
// dfs的作用:求从x,y到底部的路径最大值
int dfs (int x , int y)
{
    // 边界条件
    // 不合法的状态,我们要让他一定不被考虑。
    // 如果全部都是负数,那么返回0将是错误的结果
    if (y > x) return -inf;
    if (dp[x][y] != -1) return dp[x][y];
    if (x == n + 1) return 0;
    int ans = a[x][y] + max (dfs(x + 1 , y) , dfs(x + 1 , y + 1));
    dp[x][y] = ans;
    return ans;
}
int main()
{
    // 把所有dp置为-1
    // memset 不是什么值都可以赋的!
    // 只能初始化0 || -1
    memset(dp , -1 , sizeof dp);
    cin >> n >> m;
    for (int i = 1 ; i <= n; i ++){
        for (int j = 1 ; j <= i ; j++){
            cin >> a[i][j];
        }
    }
    cout << dfs(1 , 1) << endl;
    return 0;
}


2.递推
//数塔问题=递推版本:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
const int inf = 1e9;
// dp(x , y) 从(x , y)到最下面一层的最大值
int dp[maxn][maxn] , a[maxn][maxn];
int main()
{
    int t; cin >> t;
    while (t--){
        // 初始化
        memset (dp , 0 , sizeof dp);
        int n; cin >> n;
        for (int i = 1 ; i <= n ; i++){
            for (int j = 1 ; j <= i ; j++){
                cin >> a[i][j];
            }
        }
        for (int i = n ; i >= 1 ; i--){
            for (int j = 1 ; j <= i ; j++){
                dp[i][j] = a[i][j] + max(dp[i + 1][j] , dp[i + 1][j + 1]);
            }
        }
        cout << dp[1][1] << endl;
    }
    return 0;
}

最大子段和模型

ps:一段数最大的和

#include <bits/stdc++.h>
using namespace std;

const int maxn = 10005;
int a[maxn],b[maxn],c[maxn];
int n;

int main()
{
    cin >> n;
    for(int i = 1 ; i <= n ; i++){
        cin >> a[i];
    }
     b[1] = a[1];
     c[1] = 1;
    for(int i = 2 ;i <= n ; i++){
        b[i] = a[i]+max(b[i-1],0);//b[i]以i为结尾的最大子段和
        if(b[i]==a[i]+b[i-1])//如果选择
            c[i]=c[i-1];//c[i]以i结尾的最大子段和的起始点
        else//如果不选
            c[i]=i;
    }
    int u,s = 0;
    for(int i = 1 ; i <= n ; i++){
        if(s<b[i]){
            s=b[i];
            u=i;
        }
    }
    for(int i = c[u] ; i <= u ; i++)
        cout << a[i] <<"  ";

   cout <<endl << s << endl;
    return 0;
}





序列组合模型

ps:n个格子,每个格子可填写数[1,m].满足任意一对相邻的格子的数绝对值小于等于2的填写方案数

1.记忆化搜索
//dfs版本
#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
const int inf = 1e9;
int n , m;
// dfs (st , val) 求从st填写到1格子的所有可能方案.
int dp[105][108];
int dfs (int st , int val)
{
    if (dp[st][val] != -1) return dp[st][val];
    if (st == 0) return 1;
    int ans = 0;
    for (int i = 1 ; i <= m ; i++){
        if (abs(val - i) <= 2){
            ans += dfs(st - 1 , i);
        }
    }
    dp[st][val] = ans;
    return ans;
}
int main()
{
    memset(dp , -1 , sizeof dp);
    cin >> n >> m;
    int ans = 0;
    for (int i = 1 ; i <= m ; i++){
        ans += dfs (n - 1 , i);
    }
    cout << ans << endl;
    return 0;
}


2.递推
#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
const int inf = 1e9;
int n , m;
// dp(i , j) 代表从1号格子填写到第i号格子,且第i号格子的数为j的所有方案数.
int dp[maxn][maxn];
int main()
{
    int n , m; cin >> n >> m;
    // 边界条件,因为1之前没有格子了
    for (int i = 1 ; i <= m ; i++)
        dp[1][i] = 1;
    // 从2开始转移.
    for (int i = 2 ; i <= n ; i++){
        for (int j = 1 ; j <= m ; j++){
            for (int k = max(j - 2 , 1) ; k <= min(j + 2 , m) ; k++){
                dp[i][j] += dp[i - 1][k];
            }
        }
    }
    int ans = 0;
    for (int i = 1 ; i <= m ; i++){
        ans += dp[n][i];
    }
    cout << ans << endl;
    return 0;
}

LIS:最长上升子序列模型
#include <bits/stdc++.h>
using namespace std;
int dp[100];
int main()
{
    string a;
    cin >> a;
    int ans;
    dp[0]=1;
    for(int i=1;i<a.size();i++){
        for(int j=i-1;j>=0;j--){ // j==[1,i-1] a[j]<a[i]  O(n^2)
            if(a[j]<a[i]){
               dp[i] = max(dp[i],dp[j]+1);
            }
        }
        ans =max(ans,dp[i]);
    }
    cout << ans;
}



走格子模型

ps:只能走下和右

//dp记录到该格子有多少种方案
#include <bits/stdc++.h>
using namespace std;

int a[100][100];
int dp[100][100];
int main()
{

    int n ,m;
    cin >> n >> m;
    int n1,m1;
    cin >> n1 >>m1;

    for(int i = m ;i <= m1 ; i++)
        dp[n][i] = 1;

    for(int i =n+1 ; i <= n1 ;i++){
        for(int j = m ;j <= m1 ;j++){
            dp[i][j]=dp[i][j-1]+dp[i-1][j];
        }
    }
    cout << dp[n1][m1];
    return 0;
}

LCS:最长公共子序列模型

ps:求两个字符串最长公共序列

#include <bits/stdc++.h>
using namespace std;

int dp[1005][1005];
int main()
{
    string a,b;
    int n,m;
    cin >> n >>m;
    cin >> a;
    cin >> b;
    a='#'+a;
    b='^'+b;
    for(int i = 1 ; i <=n ; i++ ){
        for(int j = 1 ; j <=m ; j++ ){
            if(a[i]==b[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][m];
    return 0;
}





回文串模型
1.给你一个字符串。长度为2000,Q(Q<=1e6)次询问[L,R]子串是否为回文串
if(s[l]==s[r])
dp[l][r] = dp[l+1][r-1];  //O(n^2)
2.最长回文序列
//dp存最长回文序列数  a为回文数组 i为数组头 j为数组尾
if(a[i]==a[j])
dp[i][j]=dp[i+1][j-1]+1;
else 
dp[i][j]=max(dp[i+1][j],dp[i][j+1]);
背包模型

ps:造出n物品在不超过体积V的情况下,让价值(w)最大(v<=1000,n<=1000,w<=1e9)

//dfs加记忆化搜索
int dfs(st,v,w){ // st第几个  v体积  w为价值  
max(dfs(st+1,v),w[st]+dfs(st+1,v-v[st]));//返回最大价值
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爪蛙毁一生

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

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

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

打赏作者

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

抵扣说明:

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

余额充值