本文最后更新于:星期二, 八月 2日 2022, 9:32 晚上

见多了优秀的文章,再写博客的时候就会感叹自己的学识浅薄。

leetcode 198 打家劫舍

动态规划

设置dp[i]为前i个元素中打劫所得最高金额

构建状态转移方程:

dp[i] = max(dp[i-1], dp[i-2]+nums[i]);

边界条件:

dp[0] = nums[0];
dp[1] = max(nums[0], nums[1]);

代码:

class Solution {
public:
    int rob(vector<int>& nums) {
        if (nums.empty()) return 0;
        if (nums.size() == 1) return nums[0];
        vector<int> dp(nums.size(), 0);
        dp[0] = nums[0];
        dp[1] = max(nums[0], nums[1]);

        for (int i = 2; i < nums.size(); ++i) {
            dp[i] = max(dp[i-1], dp[i-2]+nums[i]);
        }

        return dp[nums.size()-1];
    }
};

leetcode 674 最长连续递增子序列

不必使用动态规划,直接一遍遍历,碰到nums[i] < nums[i+1]就递增计数器,保留计数器最大值即可:

class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        if (nums.empty()) return 0;
        int count = 1;
        int maxCount = 1;
        for (int i = 0; i < nums.size() - 1; ++i) {
            if (nums[i] < nums[i+1]) {
                ++count;
                maxCount = max(count, maxCount);
            } else {
                count = 1;
            }
        }
        return maxCount;
    }
};

后来我悟了,这就是动态规划,只不过我利用maxCount来代替了dp数组。我真是个天才(误)!

leetcode 5 最长回文子串

本来是想定义dp[i][j],表达字符串从i到j的子串中的最长回文子串的。后来想想这样定义不合适,不如把dp定义为一个bool数组,用来标记从i到j子串是否为回文串即可。

状态转移关系:dp[i][j] = dp[i+1][j-1] && (s[i] == s[j])

即s[i:j]为回文串的条件为s[i+1][j-1]为回文串,且s[i] == s[j]

边界条件:
dp[i][j] = true if i == j
dp[i][j] = false if i > j
dp[i][i+1] = (s[i] == s[i+1])

最后遍历所有dp[i][j]=true的项,返回最长的子串即可

需要注意的一点:我们在遍历双层循环的时候,应该j在外,i在内。想想为什么?如果循环结构是这样的:

for (int i = 0; i < s.size(); ++i) {
    for (int j = i+1; j < s.size(); ++j) {
        // TODO
    }
}

那 i、j的变化为:
0,0
0,1
0,2
0,3->这里就不对了,因为dp[0][3]需要用到dp[1][2]的值。而i=1时的所有dp都还没求呢。

代码:

class Solution {
public:
    string longestPalindrome(string s) {
        if (s.empty()) return "";
        int size = s.size();
        vector<vector<bool>> dp(size, vector<bool>(size, false));
        string ans = "";
        for (int j = 0; j < size; ++j) {
            for (int i = 0; i <= j; ++i) {
                if (j == i) dp[i][j] = true;
                else if (j == i+1) dp[i][j] = (s[i] == s[j]);
                else dp[i][j] = (dp[i+1][j-1]) && (s[i] == s[j]);

                if (dp[i][j] && ans.size() < j-i+1) ans = s.substr(i, j-i+1);
            }
        }
        return ans;
    }
};

leetcode 213 打家劫舍2

打家劫舍升级版,贼不能同时打劫头尾。

也好办,拆分成两个动态规划,一个规定不能打劫nums[0],另一个规定不能打劫nums[size-1],最后返回更大的那个即可。

代码:

class Solution {
public:
    int rob(vector<int>& nums) {
        if (nums.empty()) return 0;
        int size = nums.size();
        if (size == 1) return nums[0];
        if (size == 2) return max(nums[0], nums[1]);

        vector<int> dp_robfirst(size, 0);
        vector<int> dp_roblast(size, 0);

        dp_robfirst[0] = nums[0];
        dp_robfirst[1] = max(nums[0], nums[1]);
        for (int i = 2; i < size-1; ++i) {
            dp_robfirst[i] = max(dp_robfirst[i-1], dp_robfirst[i-2] + nums[i]);
        }

        dp_roblast[0] = 0;
        dp_roblast[1] = nums[1];
        for (int i = 2; i < size; ++i) {
            dp_roblast[i] = max(dp_roblast[i-1], dp_roblast[i-2] + nums[i]);
        }

        return max(dp_robfirst[size-2], dp_roblast[size-1]);
    }
};

leetcode 516 最长回文子序列

这次的dp含义为从i到j子串中最长的回文序列长度。

转移方程:

dp[i][j] = dp[i+1][j-1] if s[i] == s[j]
dp[i][j] = max(dp[i+1][j], dp[i][j-1]) if s[i] != s[j]

注意,i从大遍历到小,j从小遍历到大。最后返回dp[0][size-1]

边界条件:dp[i][j] = 1 if i == j

代码:

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        if (s.empty()) return 0;
        int size = s.size();
        vector<vector<int>> dp(size, vector<int>(size, 0));

        for (int i = size-1; i >= 0; --i) {
            for (int j = i; j < size; ++j) {
                if (i == j) dp[i][j] = 1;
                else if (s[i] == s[j]) dp[i][j] = dp[i+1][j-1] + 2;
                else dp[i][j] = max(dp[i+1][j], dp[i][j-1]);
            }
        }

        return dp[0][size-1];
    }
};

leetcode 72 编辑距离

代码:

class Solution {
public:
    int minDistance(string word1, string word2) {
        int M = word1.size();
        int N = word2.size();
        // if (word1.empty() || word2.empty()) return abs(M-N);
        vector<vector<int>> dp(M+1, vector<int>(N+1, 0));
        //initial
        for (int i = 0; i <= M; ++i) {
            dp[i][0] = i;
        }
        for (int i = 0; i <= N; ++i) {
            dp[0][i] = i;
        }
        //dp
        for (int i = 1; i <= M; ++i) {
            for (int j = 1; j <= N; ++j) {
                if (word1[i-1] == word2[j-1]) {
                    dp[i][j] = min(dp[i - 1][j - 1], 1 + dp[i - 1][j]);
                    dp[i][j] = min(dp[i][j], 1 + dp[i][j - 1]);
                } else {
                    dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j]);
                    dp[i][j] = 1 + min(dp[i][j], dp[i][j - 1]);
                }
            }
        }
        return dp[M][N];
    }
};

notes      algorithm leetcode Datawhale Dynamic Programming

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!