动态规划之想不到的递推公式(持续更新)

之前系统地写了动态规划的常见题目,已经能够解决大部分动态规划相关问题。但是,还是存在一些题目,它的递推公式很难想到。所以,新开这一篇文章来记录一下,一方面,提高从题干提取递推公式的能力;另一方面,经常回顾,避免遇到原题还不会做。

把数字翻译成字符串

题目:把数字翻译成字符串_牛客题霸_牛客网 (nowcoder.com)

  • dp数组及下标含义

dp[i]表示长度为i的字符串可能的译码种数

  • 递推关系

如果结尾的字符不为0,dp[i] += dp[i - 1]

如果最后两个字符组成的数字在10 - 26之间,dp[i] += dp[i - 2]

  • 初始化

dp[0]初始化为1(i等于2时,数字在10 - 26之间,dp[2] += dp[0],所以dp[0] = 1)、

如果nums[0]不为‘0’,dp[1] = 1

其余初始化为0

  • 遍历顺序

从2开始,从小到大

int solve(string nums) {
    vector<int> dp(nums.size() + 1, 0);
    dp[0] = 1;
    if(nums[0] != '0')
        dp[1] = 1;
    for(int i = 2; i <= nums.size(); i++){
        if(nums[i - 1] != '0')
            dp[i] += dp[i - 1];
        string t = nums.substr(i - 2, 2);
        int n = atoi(t.c_str());
        if(n >= 10 && n <= 26)
            dp[i] += dp[i - 2];
    }
    return dp[nums.size()];
}

正则表达式匹配

题目:正则表达式匹配_牛客题霸_牛客网 (nowcoder.com)

  • dp数组及下标含义

dp[i][j]表示str下标i - 1前部分与pattern下标j - 1部分是否匹配

  • 递推关系

如果str[i - 1] == pattern[j - 1]或者pattern[j - 1] == '.',dp[i][j] = dp[i - 1][j - 1]

如果pattern[j - 1] == '*',两种情况:

  • '*'表示前面的字符出现0次,j往后移两位,dp[i][j] = dp[i][j - 2]

  • '*'表示前面的字符出现多次,i往后移一位判断与'*'前一位是否匹配,即下标i与'*'后一位配对,dp[i][j] = dp[i - 1][j]

  • 初始化

dp[0][0] = true

dp[i][0] = false(i大于0)

当pattern为"a*a*"这类,dp[0][j] = true

其余为false

  • 遍历顺序

从小到大

bool match(string str, string pattern) {
    vector<vector<bool>> dp(str.length() + 1, vector<bool>(pattern.length() + 1, false));
    dp[0][0] = true;
    for(int j = 2; j <= pattern.length(); j++)
        dp[0][j] = dp[0][j - 2] && pattern[j - 1] == '*';
    for(int i = 1; i <= str.length(); i++){
        for(int j = 1; j <= pattern.length(); j++){
            if(str[i - 1] == pattern[j - 1] || pattern[j - 1] == '.')
                dp[i][j] = dp[i - 1][j - 1];
            else if(pattern[j - 1] == '*'){
                if(j > 1)
                    dp[i][j] = dp[i][j - 2];
                if(!dp[i][j] && (str[i - 1] == pattern[j - 2] || pattern[j - 2] == '.'))
                    dp[i][j] = dp[i - 1][j];
            }
        }
    }
    return dp[str.length()][pattern.length()];
}

通配符匹配

题目:通配符匹配_牛客题霸_牛客网 (nowcoder.com)

  • dp数组及下标含义

dp[i][j]表示s下标i- 1前部分与p下标j - 1前部分是否匹配

  • 递推公式

如果s[i - 1] == p[j - 1]或者p[j - 1] == '?',dp[i][j] = dp[i - 1][j - 1]

如果p[j - 1] == '*',两种情况:

  • 忽略'*',即j前移一位,dp[i][j] = dp[i][j - 1]

  • '*'匹配任意长度的字符串,即i前移一位,dp[i][j] = dp[i - 1][j]

  • 初始化

dp[0][0] = true

dp[i][0] = false(i大于0)

如果p全为'*',dp[0][j] = true

  • 遍历顺序

从小到大

bool isMatch(string s, string p) {
    vector<vector<bool>> dp(s.length() + 1, vector<bool>(p.length() + 1, false));
    dp[0][0] = true;
    for(int j = 1; j <= p.length(); j++){
        if(p[j - 1] == '*')
            dp[0][j] = true;
        else
            break;
    }
    for(int i = 1; i <= s.length(); i++){
        for(int j = 1; j <= p.length(); j++){
            if(s[i - 1] == p[j - 1] || p[j - 1] == '?')
                dp[i][j] = dp[i - 1][j - 1];
            if(p[j - 1] == '*')
                dp[i][j] = dp[i - 1][j] || dp[i][j - 1];
        }
    }
    return dp[s.length()][p.length()];
}

最长的括号子串

题目:最长的括号子串_牛客题霸_牛客网 (nowcoder.com)

  • dp数组及下标含义

dp[i]表示以下标i结尾的字符串含义的括号子串的最大长度

  • 递推公式

遇到')'才有可能是合法括号,所以我们只考虑"()"和"))"两种情况

  • "()":dp[i] = dp[i - 2] + 2

  • "))":需要先判断右边的')'是否有对应的'(',其下标为i - dp[i - 1] - 1。若匹配,则dp[i]为'('前的最长括号子串,加上')'前的最长括号子串,加上括号的长度2,dp[i] = dp[i - dp[i - 1] - 2] + dp[i - 2] + 2

  • 初始化

dp[0] = 0

如果"()",dp[1] = 2,否则dp[1] = 0

  • 遍历顺序

从小到大

int longestValidParentheses(string s) {
    if(s.length() < 2)
        return 0;
    vector<int> dp(s.length(), 0);
    int res = 0;
    if(s[0] == '(' && s[1] == ')'){
        dp[1] = 2;
        res = 2;
    }
    for(int i = 2; i < s.length(); i++){
        if(s[i] == ')' && s[i - 1] == '(')
            dp[i] = dp[i - 2] + 2;
        if(s[i] == ')' && s[i - 1] == ')' && i - dp[i - 1] >= 1 && s[i - dp[i - 1] - 1] == '('){
            if(i - dp[i - 1] == 1)
                dp[i] = dp[i - 1] + 2;
            else
                dp[i] = dp[i - dp[i - 1] - 2] + dp[i - 1] + 2;
        res = max(res, dp[i]);
    }
    return res;
}

最大正方形

题目:最大正方形_牛客题霸_牛客网 (nowcoder.com)

  • dp数组及下标含义

dp[i][j]表示以下标[i - 1][j - 1]为右下角的最大正方形的边长

  • 递推公式

向左上角找正方形,如果matrix[i][j] == '1',则可以找到正方形,dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1

  • 初始化

全部初始化为0,matrix[i][j] == '0'时,边长为0

  • 遍历顺序

从小到大

int solve(vector<vector<char> >& matrix) {
    if(matrix.size() == 0)
        return 0;
    vector<vector<int>> dp(matrix.size() + 1, vector<int>(matrix[0].size() + 1, 0));
    int edge = 0;
    for(int i = 1; i <= matrix.size(); i++){
        for(int j = 1; j <= matrix[0].size(); j++){
            if(matrix[i - 1][j - 1] == '1')
                dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1;
            edge = max(edge, dp[i][j]);
        }
    }
    return edge * edge;
}

最大矩形

题目:最大矩形_牛客题霸_牛客网 (nowcoder.com)

  • dp数组及下标含义

dp[i][j]表示以下标[i, j]为右下角的矩形的pair,记录长和宽

  • 递推公式

如果matrix[i][j] == '1',从左方和上方找最大矩形

  • 初始化

dp[0][j]长为连续'1'的长度,宽为1

dp[i][0]长为1,宽为连续'1'的长度

  • 遍历顺序

从小到大

int maximalRectangle(vector<vector<int> >& matrix) {
    int res = 0, count = 0;
    vector<vector<pair<int, int>>> dp(matrix.size(),vector<pair<int, int>>(matrix[0].size()));
    for (int i = 0; i < matrix.size(); i++) {
        if (matrix[i][0] == 1)
            count++;
        else
            count = 0;
        if (res < count)
            res = count;
        dp[i][0] = pair<int, int>(1, count);
    }
    count = 0;
    for (int j = 0; j < matrix[0].size(); j++) {
        if (matrix[0][j] == 1)
            count++;
        else
            count = 0;
        if (res < count)
            res = count;
        dp[0][j] = pair<int, int>(count, 1);
    }
    for (int i = 1; i < matrix.size(); i++) {
        for (int j = 1; j < matrix[0].size(); j++) {
            if (matrix[i][j] == 0)
                dp[i][j] = pair<int, int>(0, 0);
            else {
                dp[i][j] = pair<int, int>(1, 1);
                if (dp[i - 1][j].first != 0 && dp[i - 1][j].second != 0) {
                    int width = 0, height = 0;
                    while (width < dp[i - 1][j].first && j - width >= 0) {
                        if (matrix[i][j - width] == 1)
                            width++;
                        else
                            break;
                    }
                    while (height <= dp[i - 1][j].second && i - height >= 0) {
                        if (matrix[i - height][j] == 1)
                            height++;
                        else
                            break;
                    }
                    if(dp[i][j].first * dp[i][j].second < width * height)
                        dp[i][j] = pair<int, int>(width, height);
                } 
                if(dp[i][j -1].first != 0 && dp[i][j - 1].second != 0){
                    int width = 0, height = 0;
                    while (width < dp[i - 1][j].first && j - width >= 0) {
                        if (matrix[i][j - width] == 1)
                            width++;
                        else
                            break;
                    }
                    while (height <= dp[i - 1][j].second && i - height >= 0) {
                        if (matrix[i - height][j] == 1)
                            height++;
                        else
                            break;
                    }
                    if(dp[i][j].first * dp[i][j].second < width * height)
                        dp[i][j] = pair<int, int>(width, height);
                }
            }
            if (res < dp[i][j].first * dp[i][j].second)
                res = dp[i][j].first * dp[i][j].second;
        }
    }
    return res;
}

连续子数组的最大乘积

题目:连续子数组的最大乘积_牛客题霸_牛客网 (nowcoder.com)

  • dp数组及下标含义

pos[i]表示到下标i时的最大乘积,neg[i]表示到下标i时的最小乘积

  • 递推公式

如果nums[i]为非负数,则pos[i] = max(pos[i - 1] * nums[i], nums[i]),neg[i] = min(neg[i - 1] * nums[i], nums[i])

如果nums[i]为负数,则pos[i] = max(neg[i - 1] * nums[i], nums[i]),neg[i] = min(pos[i] * nums[i], nums[i])

  • 初始化

pos[0]和neg[0]初始化为nums[0]

  • 遍历顺序

从小到大

int maxProduct(vector<int>& nums) {
    int res = nums[0];
    vector<int> pos(nums.size(), nums[0]), neg(nums.size(), nums[0]);
    for(int i = 1; i < nums.size(); i++){
        if(nums[i] >= 0){
            pos[i] = max(pos[i - 1] * nums[i], nums[i]);
            neg[i] = min(neg[i - 1] * nums[i], nums[i]);
        }
        else{
            pos[i] = max(neg[i - 1] * nums[i], nums[i]);
            neg[i] = min(pos[i - 1] * nums[i], nums[i]);
        }
        res = max(res, nums[i]);
    }
    return res;
}

填充数组

题目:填充数组_牛客题霸_牛客网 (nowcoder.com)

  • dp数组及下标含义

dp[i][j]表示有i个0需要填充,可以从j个数取值的方案数

  • 递推公式

dp[i][j] = = dp[i - 1][j] + = dp[i - 1][j] + dp[i][j - 1]

  • 初始化

dp[1][j] = j(为什么不是初始化dp[0][j]?因为dp[0][j] = 0,i= 1时不满足递推公式), dp[i][0] = 0

  • 遍历顺序

从小到大

int FillArray(vector<int>& a, int k) {
    int mod = 1000000007, i = 0, start, minv, end, maxv, res = 1;
    vector<vector<int>> dp(a.size() + 1, vector<int>(k + 1, 0));
    for(int j = 1; j <= k; j++)
        dp[1][j] = j;
    for(int i = 2; i <= a.size(); i++){
        for(int j = 1; j <= k; j++){
            dp[i][j] = (dp[i][j - 1] + dp[i - 1][j]) % mod;
        }
    }
    while(i < a.size()){
        while(i < a.size() && a[i] != 0)
            i++;
        if(i == a.size())
            break;
        start = i;
        minv = (i == 0? 1:a[i - 1]);
        while(i < a.size() && a[i] == 0)
            i++;
        end = i;
        maxv = (i == a.size()? k:a[i]);
        res = ((long long)res * dp[end - start][maxv - minv + 1]) % mod;
    }
    return res;
}

压缩字符串2

题目:压缩字符串(二)_牛客题霸_牛客网 (nowcoder.com)

  • dp数组及下标含义

dp[i][j]表示长度为i的字符串,删除j个字符,压缩后的字符串长度

  • 递推公式

  • 如果删除当前字符,dp[i][j + 1] = dp[i - 1][j]

  • 如果不删除当前字符,如果后面与当前字符不同的个数小于j,则全部删除

  • 初始化

dp[0][j] = 0

其余初始化为INT_MAX >> 1

  • 遍历顺序

从小到大

int compressString(string s, int k) {
    vector<vector<int>> dp(s.length() + 1, vector<int>(k + 1, INT_MAX >> 1));
    dp[0][0] = 0;
    for(int i = 1; i <= s.length(); i++){
        for(int j = 0; j <= k && j <= k; j++){
            if(j < k)
                dp[i][j + 1] = min(dp[i][j + 1], dp[i - 1][j]);
            int same = 0, diff = 0;
            for(int m = i; m <= s.length(); m ++){
                if(s[m - 1] == s[i - 1])
                    same++;
                else
                    diff++;
                if(j + diff > k)
                    break;
                dp[m][j + diff] = min(dp[m][j + diff], len(same) + dp[i - 1][j]);
            }
        }
    }
    return dp[s.length()][k];
}
int len(int same){
    if(same == 1)
        return 1;
    if(same < 10)
        return 2;
    if(same < 100)
        return 3;
    return 4;
}

信封嵌套问题

题目:信封嵌套问题_牛客题霸_牛客网 (nowcoder.com)

信封A的长和宽都比B的大,就可以嵌套。所以按长从小到大进行排序,就可以转换为宽的最长连续子序列问题

int maxLetters(vector<vector<int> >& letters) {
    sort(letters.begin(), letters.end(), Compare);
    int res = 1;
    vector<int> dp(letters.size(), 1);
    for(int i = 1; i < letters.size(); i++){
        for(int j = 0; j < i; j++){
            if(letters[i][1] > letters[j][1])
                dp[i] = max(dp[i], dp[j] + 1);
        }
        if(res < dp[i])
            res = dp[i];
    }
    return res;
}
static bool Compare(vector<int> x, vector<int> y){
    if(x[0] == y[0])
        return x[1] > y[1];
    return x[0] < y[0];
}

戳气球

题目:312. 戳气球 题解 - 力扣(LeetCode)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值