• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
jk-2048
博客园    首页    新随笔    联系   管理    订阅  订阅
动态规划算法

前言与重点

1.DP数组

dp[ i ] [ j ]
 

2.递推公式(状态转移公式)

 

3.算法步骤

1.确定DP数组及其下标的含义
2.确定递推公式(状态转移公式)
3.dp数组如何初始化
4.确定遍历顺序
5.举例推导dp数组
 
 

动态规划理论基础

什么是动态规划

动态规划,英⽂:Dynamic Programming,简称DP,如果某⼀问题有很多重叠⼦问题,使
⽤动态规划是最有效的。
所以动态规划中每⼀个状态⼀定是由上⼀个状态推导出来的,这⼀点就区分于贪心,贪心没
有状态推导,而是从局部直接选最优的,
(降谷羽:感觉可以称之为空间递推算法)
 

示例算法:力扣509 斐波那契数列

1.确定dp数组含义

这里dp[i] 的含义为斐波那契数列的第i个数的值
 

2.确定状态转移方程(递推式)

3.初始化dp数组

 

4.确定遍历顺序

按依赖关系来确定递推顺序

5.对dp数组递推填充

 
 

dp+字符串 经典案例:力扣5 最长回文子串

1.确定dp数组含义

建立二维dp数组 P
p ( i,j ) 表示字符串 sss 的第 i 到 j 个字母组成的子串(下文表示成 s[i:j])是否为回文串

2.确定状态转移方程(递推式)

由于对于一个子串而言,如果它是回文串,并且长度大于 2,那么将它首尾的两个字母去除之后,它仍然是个回文串。例如对于字符串 “ababa”,如果我们已经知道 “bab” 是回文串,那么 “ababa” 一定是回文串
回文串掐头去尾之后,还是回文串!(充分必要)
P (i,j ) = P (i +1,j −1) && ( S[i] == S[j] )
只有 s[i+1:j−1] 是回文串,并且 s 的第 i和 j个字母相同时,s[i:j]才会是回文串。
(不加头尾时是回文串 && 头尾值相等)
 

3.初始化dp数组

长度为1的一定是回文串
长度为2的,两字符相等就是回文串
vector<vector<int>> dp(n, vector<int>(n));  //创建二维dp数组
    // 初始化:所有长度为 1 的子串都是回文串
for (int i = 0; i < n; i++) 
{
    dp[i][i] = true;
}

4.确定遍历顺序

先从小到大枚举子串长度,再从前往后枚举起始点下标
起始点+长度就可以确定唯一一个子串
// 递推开始
        // 先枚举子串长度
        for (int L = 2; L <= n; L++) 
        {
            // 枚举左边界,左边界的上限设置可以宽松一些
            for (int i = 0; i < n; i++) 
            {
                // 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
                int j = L + i - 1;
                // 如果右边界越界,就可以退出当前循环
                if (j >= n) 
                {
                    break;
                }

                if (s[i] != s[j])  //若头尾不相等
                {
                    dp[i][j] = false;
                } 
                else   //若头尾相等
                {
                    if (j - i < 3) 
                    {
                        dp[i][j] = true;
                    } 
                    else 
                    {
                        dp[i][j] = dp[i + 1][j - 1];  //递推式
                    }
                }

               //记录最长回文子串  的起始点、长度
                if (dp[i][j] && j - i + 1 > maxLen) 
                {
                    maxLen = j - i + 1;
                    begin = i;
                }
最后输出答案:
递推到全字符串长度,将结果输出
return s.substr(begin, maxLen);
 
 
 
dp案例:
粉刷匠
#include<bits/stdc++.h>
using namespace std;

using namespace std;

int main() 
{
    int N, M, T;
    cin >> N >> M >> T;  // 输入N,M和T

    vector<string> boards(N);  // 存储木板的向量
    for (int i = 0; i < N; i++) {
        cin >> boards[i];  // 输入每个木板的颜色信息
    }

    // 初始化dp数组,dp[i][j]表示前i个木板使用j次粉刷能正确粉刷的最多格子数
    vector<vector<int>> dp(N + 1, vector<int>(T + 1, 0));

    // 动态规划
    for (int i = 1; i <= N; i++) 
        {
        for (int j = 1; j <= T; j++) 
                {
            // 不对第i个木板进行粉刷,直接继承前一个木板的结果
            dp[i][j] = dp[i - 1][j];

            // 对第i个木板进行粉刷
            for (int k = 1; k <= M; k++) 
                        {
                if (j >= k)
                                 {
                    // 计算选择粉刷的格子数
                    int count_zero = count(boards[i - 1].begin() + k - 1, boards[i - 1].end(), '0');
                    int count_one = count(boards[i - 1].begin() + k - 1, boards[i - 1].end(), '1');
                    int count = count_zero + count_one;

                    // 更新最大的正确粉刷格子数
                    dp[i][j] = max(dp[i][j], dp[i - 1][j - k] + count);
                }
            }
        }
    }

    // 输出结果
    cout << dp[N][T] << endl;

    return 0;
}

 

posted on 2024-02-23 18:28  JK降谷羽  阅读(96)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3