C++秋招刷题 | 动态规划与背包问题

26 篇文章 0 订阅
20 篇文章 0 订阅

动态规划

斐波那契数列

斐波那契数

斐波那契数
在这里插入图片描述

  • 动态规划
class Solution {
public:
    int fib(int n) {
        if (n < 2) {
            return n;
        }
        int p = 0, q = 0, r = 1;
        for (int i = 2; i <= n; ++i) {
            p = q; 
            q = r; 
            r = p + q;
        }
        return r;
    }
};

第 N 个泰波那契数

第 N 个泰波那契数
在这里插入图片描述

  • 斐波那契稍微改动
class Solution {
public:
    int tribonacci(int n) {
        if(n<2)return n;
        int hair = 0, pre = 1, cur = 1, ans = cur;
        for(int i = 3; i <= n; ++i){
            ans = hair + pre + cur;
            hair = pre;
            pre = cur;
            cur = ans;
        }
        return ans;
    }
};

丑数

丑数Ⅱ

  1. 丑数 II - 力扣(LeetCode)
    https://leetcode-cn.com/problems/ugly-number-ii/

给你一个整数 n ,请你找出并返回第 n 个 丑数 。

丑数 就是只包含质因数 2、3 和/或 5 的正整数。

  • set保存已有的数;
  • 优先队列保障从小到大取数;
class Solution {
public:
    int nthUglyNumber(int n) {
        long ugly=1;
        vector<long> factors={2L,3L,5L};
        unordered_set<long> s;
        priority_queue<long,vector<long>,greater<long>> p;
        s.insert(1L);
        p.push(1L);
        for(int i=0;i<n;++i){
            ugly=p.top();
            p.pop();
            for(int factor:factors){
                long tmp=ugly*factor;
                if(s.find(tmp)==s.end()){
                    s.insert(tmp);
                    p.push(tmp);
                }
            }
        }
        return ugly;
    }
};

杨辉三角

杨辉三角

  1. 杨辉三角 - 力扣(LeetCode)
    https://leetcode-cn.com/problems/pascals-triangle/submissions/
  • 递归求解
class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> ans;
        for(int i=1;i<=numRows;++i){
            vector<int> tmp(i,1);
            for(int j=1;j<i-1;++j){
                tmp[j]=ans[i-2][j-1]+ans[i-2][j];
            }
            ans.emplace_back(tmp);
        }
        return ans;
    }
};

杨辉三角Ⅱ

  1. 杨辉三角 II - 力扣(LeetCode)
    https://leetcode-cn.com/problems/pascals-triangle-ii/submissions/
  • 只需要输出rowIndex层,可以把空间简化:
  1. 第一步:每一层只与上一层有关,利用滚动数组可以简化为2个一维vector;
  2. 第二步:一层里的第i个元素只与上一层的第i-1个和第i个的元素有关,简化成1个一维vector;
class Solution {
public:
    vector<int> getRow(int rowIndex) {
        vector<int> ans(rowIndex+1);
        ans[0]=1;
        for(int i=0;i<=rowIndex;++i){
            for(int j=i;j>0;--j){
                ans[j]+=ans[j-1];
            }
        }
        return ans;
    }
};

爬楼梯

爬楼梯

同斐波那契数
爬楼梯
在这里插入图片描述

class Solution {
public:
    int climbStairs(int n) {
        int p = 0, q = 0, r = 1;
        for (int i = 1; i <= n; ++i) {
            p = q; 
            q = r; 
            r = p + q;
        }
        return r;
    }
};

最小花费爬楼梯

最小花费爬楼梯
在这里插入图片描述

    int minCostClimbingStairs(vector<int>& cost) {
        int len = cost.size();
        int dp[len+1];
        dp[0]=0;
        dp[1]=0;
        for(int i=2;i<len+1;++i){
            dp[i] = min(dp[i-2]+cost[i-2],dp[i-1]+cost[i-1]);
        }
        return dp[len];
    }

买卖股票

买卖股票的最佳时机

121. 买卖股票的最佳时机 - 力扣(LeetCode)

  • 一次遍历查询
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int len=prices.size();
        int pre=prices[0],res=0;
        for(auto price:prices){
            res=max(price-pre,res);
            pre=min(pre,price);
        }
        return res;
    }
};

买卖股票的最佳时机 II

122. 买卖股票的最佳时机 II - 力扣(LeetCode)

  • 贪心
class Solution {
public:
    int maxProfit(vector<int>& prices) {   
        int ans = 0;
        int n = prices.size();
        for (int i = 1; i < n; ++i) {
            ans += max(0, prices[i] - prices[i - 1]);
        }
        return ans;
    }
};
  • 动态规划
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        int dp[n][2];
        dp[0][0] = 0, dp[0][1] = -prices[0];
        for (int i = 1; i < n; ++i) {
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
            dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
        }
        return dp[n - 1][0];
    }
};

最佳买卖股票时机含冷冻期

309. 最佳买卖股票时机含冷冻期 - 力扣(LeetCode)

  • 在 买卖股票的最佳时机 II的基础上,处理一下购买股票的冷却期
class Solution {
public:
//[1,2,4]
//[2,1,4]
    int maxProfit(vector<int>& prices) {
        int len=prices.size();
        int dp[len][2];
        dp[0][0]=0;//第一天没买股票
        dp[0][1]=-prices[0];//第一天买了股票
        for(int i=1;i<len;++i){
            dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]);
            dp[i][1]=(i-2>=0)?(max(dp[i-1][1],dp[i-2][0]-prices[i])):max(dp[i-1][1],-prices[i]);
        }
        return dp[len-1][0];
    }
};

买卖股票的最佳时机含手续费

714. 买卖股票的最佳时机含手续费 - 力扣(LeetCode)

  • 在 买卖股票的最佳时机 II的基础上,处理一下费率即可
class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int len=prices.size();
        int dp[len][2];
        dp[0][0]=0;
        dp[0][1]=-prices[0];
        for(int i=1;i<len;++i){
            dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]-fee);
            dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]);
        }
        return dp[len-1][0];
    }
};

戳气球

  1. 戳气球 - 力扣(LeetCode)
    https://leetcode-cn.com/problems/burst-balloons/

在这里插入图片描述

  • 记忆化搜索
  • 将戳气球问题改为添气球问题。自顶向下。
class Solution {
private:
    vector<int> val;
    vector<vector<int>> rec;
public:
    int solve(int left,int right){
        if(left>=right-1){
            return 0;
        }
        if(rec[left][right]!=-1){
            return rec[left][right];
        }
        for(int i=left+1;i<right;++i){
            int sum=val[i]*val[left]*val[right];
            sum+=solve(left,i)+solve(i,right);
            rec[left][right]=max(rec[left][right],sum);
        }
        return rec[left][right];
    }
    int maxCoins(vector<int>& nums) {
        int n=nums.size();
        val.resize(n+2);
        val[0]=val[n+1]=1;
        for(int i=1;i<=n;++i){
            val[i]=nums[i-1];
        }
        rec.resize(n+2,vector<int>(n+2,-1));
        return solve(0,n+1);
    }
};
  • 动态规划
  • 通过改变计算次序,完成从小往大,自底向上,减少函数栈的使用
class Solution {
public:
    int maxCoins(vector<int>& nums) {
        int n = nums.size();
        vector<vector<int>> rec(n + 2, vector<int>(n + 2));
        vector<int> val(n + 2);
        val[0] = val[n + 1] = 1;
        for (int i = 1; i <= n; i++) {
            val[i] = nums[i - 1];
        }
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i + 2; j <= n + 1; j++) {
                for (int k = i + 1; k < j; k++) {
                    int sum = val[i] * val[k] * val[j];
                    sum += rec[i][k] + rec[k][j];
                    rec[i][j] = max(rec[i][j], sum);
                }
            }
        }
        return rec[0][n + 1];
    }
};

打家劫舍

打家劫舍1

打家劫舍
在这里插入图片描述

  • 使用数组,额外空间
class Solution {
    /**
    [0]
    [1,2]
    [2,1,1,2]
    */
public:
    int rob(vector<int>& nums) {
        if(nums.size()==0)return 0;
        if(nums.size()==1)return nums[0];
        vector<int> dp(nums.size()+1,0);
        dp[0]=nums[0];dp[1]=max(nums[1],dp[0]);
        for(int i=2;i<nums.size();i++){
            dp[i] = max(dp[i-2]+nums[i],dp[i-1]);
        }
        return dp[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];
        }
        int first = nums[0], second = max(nums[0], nums[1]);
        for (int i = 2; i < size; i++) {
            int temp = second;
            second = max(first + nums[i], second);
            first = temp;
        }
        return second;
    }
};

打家劫舍2

打家劫舍

class Solution {
public:
    int robRange(vector<int>& nums, int start, int end) {
        int first = nums[start], second = max(nums[start], nums[start + 1]);
        for (int i = start + 2; i <= end; i++) {
            int temp = second;
            second = max(first + nums[i], second);
            first = temp;
        }
        return second;
    }

    int rob(vector<int>& nums) {
        int length = nums.size();
        if (length == 1) {
            return nums[0];
        } else if (length == 2) {
            return max(nums[0], nums[1]);
        }
        return max(robRange(nums, 0, length - 2), robRange(nums, 1, length - 1));
    }
};

背包问题

完全背包

有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积是c,价值是w。将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。

购物车

购物单_牛客题霸_牛客网

  • 主副件捆绑购买,背包问题
#include<bits/stdc++.h>
using namespace std;
/**
6000 15
100 3 0
400 5 0
300 5 0
1400 2 3
500 2 2
800 2 3
1400 5 0
300 5 0
1400 3 0
500 2 0
1800 4 0
440 5 10
1340 5 10
430 3 0
500 2 0
*/
int main(){
    int N,m;//N钱数,m物品个数
    cin>>N>>m;
    int val[60][3]={0},cos[60][3]={0};//分别代表价格、物品重要度、主件标识(0为主键)
    int total[3200]={0};//题目提示都是十的倍数
    //最多两个附件
    for(int i=1;i<=m;++i){
        int v,p,q;
        cin>>v>>p>>q;
        if(q!=0){
            //不是主件
            if(val[q][1]==0){
                //第一个附件
                val[q][1]=v*p;
                cos[q][1]=v;
            }else{
                val[q][2]=v*p;
                cos[q][2]=v;
            }
        }else{
            val[i][0]=v*p;
            cos[i][0]=v;
        }
    }
    for(int i=1;i<=m;++i){//遍历所有物品
        for(int j=N/10;j>=cos[i][0]/10;--j){
            //只选主件
            if(j>=cos[i][0]/10){
                total[j]=max(total[j],val[i][0]+total[j-cos[i][0]/10]);
            }
            //选主+1附件
            int tmp=(cos[i][0]+cos[i][1])/10;
            if(cos[i][1]!=0&&j>=tmp){
                total[j]=max(total[j],val[i][0]+val[i][1]+total[j-tmp]);
            }
            //主+2附件
            tmp=(cos[i][0]+cos[i][1]+cos[i][2])/10;
            if(cos[i][2]!=0&&j>=tmp){
                total[j]=max(total[j],val[i][0]+val[i][1]+val[i][2]+total[j-tmp]);
            }
        }
    }
    cout<<total[N/10]<<endl;
    return 0;
}
大礼包

https://leetcode-cn.com/problems/shopping-offers/
在这里插入图片描述

  • dfs
class Solution {
private:
    int ans=INT_MAX;
    int getNeedsCost(vector<int>& price, vector<int>& needs){
        int total=0;
        for(int i=0;i<needs.size();++i){
            total+=needs[i]*price[i];
        }
        return total;
    }
    void shooppingSpecials(int cost, vector<int>& price, vector<vector<int>>& special, vector<int>& needs){
        //获得当前方案的开销(补充剩余物品)
        int total=cost+getNeedsCost(price,needs);
        ans=total<ans?total:ans;
        //遍历每种情况
        for(int i=0;i<special.size();++i){
            //满足条件时
            bool flag=true;
            vector<int>& tmp=special[i];
            for(int i=0;i<tmp.size()-1;++i){
                if(needs[i]-tmp[i]<0){
                    flag=false;
                    break;
                }
            }
            if(flag){
                for(int i=0;i<tmp.size()-1;++i){
                    needs[i]-=tmp[i];
                }
                shooppingSpecials(cost+tmp[tmp.size()-1],price,special,needs);
                //回溯
                for(int i=0;i<tmp.size()-1;++i){
                    needs[i]+=tmp[i];
                }                
            }
        }
    }
public:
    int shoppingOffers(vector<int>& price, vector<vector<int>>& special, vector<int>& needs) {
        //先找大礼包并补充剩余物品
        shooppingSpecials(0,price,special,needs);
        return ans;
    }
};
  • 官方题解,dfs+记忆化搜索
class Solution {
public:
    map<vector<int>, int> memo;

    int shoppingOffers(vector<int>& price, vector<vector<int>>& special, vector<int>& needs) {
        int n = price.size();

        // 过滤不需要计算的大礼包,只保留需要计算的大礼包
        vector<vector<int>> filterSpecial;
        for (auto & sp : special) {
            int totalCount = 0, totalPrice = 0;
            for (int i = 0; i < n; ++i) {
                totalCount += sp[i];
                totalPrice += sp[i] * price[i];
            }
            if (totalCount > 0 && totalPrice > sp[n]) {
                filterSpecial.emplace_back(sp);
            }
        }

        return dfs(price, special, needs, filterSpecial, n);
    }

    // 记忆化搜索计算满足购物清单所需花费的最低价格
    int dfs(vector<int> price,const vector<vector<int>> & special, vector<int> curNeeds, vector<vector<int>> & filterSpecial, int n) {
        if (!memo.count(curNeeds)) {
            int minPrice = 0;
            for (int i = 0; i < n; ++i) {
                minPrice += curNeeds[i] * price[i]; // 不购买任何大礼包,原价购买购物清单中的所有物品
            }
            for (auto & curSpecial : filterSpecial) {
                int specialPrice = curSpecial[n];
                vector<int> nxtNeeds;
                for (int i = 0; i < n; ++i) {
                    if (curSpecial[i] > curNeeds[i]) { // 不能购买超出购物清单指定数量的物品
                        break;
                    }
                    nxtNeeds.emplace_back(curNeeds[i] - curSpecial[i]);
                }
                if (nxtNeeds.size() == n) { // 大礼包可以购买
                    minPrice = min(minPrice, dfs(price, special, nxtNeeds, filterSpecial, n) + specialPrice);
                }
            }
            memo[curNeeds] = minPrice;
        }
        return memo[curNeeds];
    }
};


作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/shopping-offers/solution/da-li-bao-by-leetcode-solution-p1ww/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
零钱兑换
  • 给钱的种类,无限数量,兑换目标值,求最小兑换数
class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount+1,INT_MAX/3);
        dp[0]=0;
        for(int i=1;i<amount+1;++i){
            for(int coin:coins){
                if(i-coin>=0){
                    dp[i]=min(dp[i],dp[i-coin]+1);
                }
            }
        }
        return dp[amount]==INT_MAX/3?-1:dp[amount];
    }
};
零钱兑换 II
  1. 零钱兑换 II - 力扣(LeetCode)
    https://leetcode-cn.com/problems/coin-change-2/
  • 求零钱兑换的组合数
class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<int> dp(amount+1);
        dp[0]=1;
        for(int coin:coins){
            for(int i=coin;i<amount+1;++i){
                if(i-coin>=0&&dp[i-coin]>0){
                    dp[i]+=dp[i-coin];
                }
            }
        }
        return dp[amount];
    }
};

子序列

最大和

最大子序和

最大子序和

  • Kadane 算法模板
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int pre = 0, maxAns = nums[0];
        for (const auto &x: nums) {
            pre = max(pre + x, x);
            maxAns = max(maxAns, pre);
        }
        return maxAns;
    }
};
环形子数组的最大和

918. 环形子数组的最大和 - 力扣(LeetCode)

  • 与上题Kadane 出入的地方在于,一个是子序列和一个是子数组和;
  • 需要分类讨论环形子数组:
  1. 不环形的最大值;
  2. 环形的最大值;
class Solution {
public:
    int maxSubarraySumCircular(vector<int>& nums) {
        int len=nums.size();
        //先计算,没有环形的情况下的最大子数组和
        int ans=nums[0],pre=nums[0];
        for(int i=1;i<len;++i){
            pre=nums[i]+max(pre,0);
            ans=max(ans,pre);
        }
        //cout<<ans<<endl;
        //考虑环形,既左端加右端
        //1、计算右端和
        // rightsums[i] = A[i] + A[i+1] + ... + A[N-1]
        vector<int> rightSum(len,0);
        rightSum[len-1]=nums[len-1];
        for(int i=len-2;i>=0;--i){
            rightSum[i]=rightSum[i+1]+nums[i];
        }
        //2、计算右端和最大值
        vector<int> rightMax(len,0);
        rightMax[len-1]=nums[len-1];
        for(int i=len-2;i>=0;--i){
            rightMax[i]=max(rightMax[i+1],rightSum[i]);
        }
        //3、计算环形最大和
        int leftSum=0;
        for(int i=0;i<len-2;i++){
            leftSum+=nums[i];
            ans=max(ans,leftSum+rightMax[i+2]);
        }
        return ans;
    }
};

回文

最长回文子串
  1. 最长回文子串 - 力扣(LeetCode)
    https://leetcode-cn.com/problems/longest-palindromic-substring/
  • 在状态转移方程中,需要通过回文长度的遍历,从长度较短的字符串向长度较长的字符串进行转移的。
class Solution {
public:
    string longestPalindrome(string s) {
        int begin=0;
        int s_max=1;
        int len=s.size();
        vector<vector<int>> dp(len+1,vector<int>(len+1));
        for(int i=0;i<len;++i){
            dp[i][i]=1;
        }
        for(int L=2;L<=len;++L){
            for(int i=0;i<len;++i){
                //右边界
                int j=i+L-1;
                if(j>=len)break;
                if(s[i]!=s[j]){
                    dp[i][j]=0;
                }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>s_max){
                    s_max=j-i+1;
                    begin=i;
                }
            }
        }
        return s.substr(begin,s_max);
    }
};
  • 也可以从后往前,从长度较短的字符串向长度较长的字符串进行转移的。
class Solution {
public:
    string longestPalindrome(string s) {
        int begin=0;
        int s_max=1;
        int len=s.size();
        vector<vector<int>> dp(len+1,vector<int>(len+1));
        for(int i=len-1;i>=0;--i){
            dp[i][i]=1;
            for(int j=i+1;j<len;++j){
                // cout<<i<<" "<<j<<" "<<s[i]<<" "<<s[j]<<endl;
                if(s[i]==s[j]){
                    if(j-i<3){
                        dp[i][j]=1;
                    }else{
                        dp[i][j]=dp[i+1][j-1];
                    }
                    if(dp[i][j]==1&&j-i+1>s_max){
                        begin=i;
                        s_max=j-i+1;
                    }
                    // cout<<"dp["<<i<<"]["<<j<<"]="<<dp[i][j]<<endl;
                }else{
                    dp[i][j]=0;
                }
            }
        }
        return s.substr(begin,s_max);
    }
};
  • 法2,中心扩散
class Solution {
public:
    string longestPalindrome(string s) {
        int len=s.size();
        if(len==0||len==1)
            return s;
        int start=0;//记录回文子串起始位置
        int end=0;//记录回文子串终止位置
        int mlen=0;//记录最大回文子串的长度
        for(int i=0;i<len;i++)
        {
            int len1=expendaroundcenter(s,i,i);//一个元素为中心
            int len2=expendaroundcenter(s,i,i+1);//两个元素为中心
            mlen=max(max(len1,len2),mlen);
            if(mlen>end-start+1)
            {
                start=i-(mlen-1)/2;
                end=i+mlen/2;
            }
        }
        return s.substr(start,mlen);
        //该函数的意思是获取从start开始长度为mlen长度的字符串
    }
private:
    int expendaroundcenter(string s,int left,int right)
    //计算以left和right为中心的回文串长度
    {
        int L=left;
        int R=right;
        while(L>=0 && R<s.length() && s[R]==s[L])
        {
            L--;
            R++;
        }
        return R-L-1;
    }
};

最长回文子序列
  1. 最长回文子序列 - 力扣(LeetCode)
    https://leetcode-cn.com/problems/longest-palindromic-subsequence/
class Solution {
public:
    int longestPalindromeSubseq(string A) {
        int len=A.size();
        vector<vector<int>> dp(len,vector<int>(len));
        
        for(int i=len-1;i>=0;--i){
            dp[i][i]=1;
            for(int j=i+1;j<len;++j){
                if(A[i]==A[j]){
                    if(j-i+1<3){dp[i][j]=2;continue;}
                    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][len-1];
    }
};

递增子序列

  1. 最长递增子序列 - 力扣(LeetCode)
    https://leetcode-cn.com/problems/longest-increasing-subsequence/
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int len=nums.size();
        vector<int> dp(len);
        int ans=1;
        for(int i=0;i<len;++i){
            dp[i]=1;
            for(int j=0;j<i;++j){
                if(nums[j]<nums[i]){
                    dp[i]=max(dp[i],dp[j]+1);
                }
            }
            ans=max(ans,dp[i]);
        }
        return ans;
    }
};

解码

解码方法

  1. 解码方法 - 力扣(LeetCode)
    https://leetcode-cn.com/problems/decode-ways/
class Solution {
public:
    int numDecodings(string s) {
        int len=s.size();
        vector<int> dp(len+1);
        dp[0]=1;
        for(int i=1;i<len+1;++i){
            if(s[i-1]!='0'){
                dp[i]+=dp[i-1];
            }
            if(i>1&&s[i-2]!='0'&&((s[i-2]-'0')*10+(s[i-1]-'0')<=26)){
                dp[i]+=dp[i-2];
            }
        }
        return dp[len];
    }
};

二叉搜索树

不同的二叉搜索树

  1. 不同的二叉搜索树 - 力扣(LeetCode)
    https://leetcode-cn.com/problems/unique-binary-search-trees/
class Solution {
public:
    int numTrees(int n) {
        vector<int> ans(n+1,0);
        ans[0]=1,ans[1]=1;
        for(int i=2;i<=n;++i){
            for(int j=1;j<=i;j++){
                ans[i]+=ans[j-1]*ans[i-j];
            }
        }
        return ans[n];
    }
};

不同的二叉搜索树Ⅱ

  1. 不同的二叉搜索树 II - 力扣(LeetCode)
    https://leetcode-cn.com/problems/unique-binary-search-trees-ii/submissions/
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<TreeNode*> generateTrees(int start, int end) {
        if (start > end) {
            return { nullptr };//不能返回空{},会导致for循环遍历不到元素
        }
        vector<TreeNode*> allTrees;
        // 枚举可行根节点
        for (int i = start; i <= end; i++) {
            // 获得所有可行的左子树集合
            vector<TreeNode*> leftTrees = generateTrees(start, i - 1);

            // 获得所有可行的右子树集合
            vector<TreeNode*> rightTrees = generateTrees(i + 1, end);

            // 从左子树集合中选出一棵左子树,从右子树集合中选出一棵右子树,拼接到根节点上
            for (auto& left : leftTrees) {
                for (auto& right : rightTrees) {
                    TreeNode* currTree = new TreeNode(i);
                    currTree->left = left;
                    currTree->right = right;
                    allTrees.emplace_back(currTree);
                }
            }
        }
        return allTrees;
    }

    vector<TreeNode*> generateTrees(int n) {
        if (!n) {
            return {};
        }
        return generateTrees(1, n);
    }
};

路径问题

下降路径最小和

  1. 下降路径最小和 - 力扣(LeetCode)
    https://leetcode-cn.com/problems/minimum-falling-path-sum/submissions/
  • 自底向上计算开销
class Solution {
public:
    int minFallingPathSum(vector<vector<int>>& matrix) {
        int ans=INT_MAX;
        int len=matrix.size();
        for(int i=len-2;i>=0;--i){
            for(int j=0;j<len;++j){
                int bst=matrix[i+1][j];
                if(j<len-1){
                    bst=min(bst,matrix[i+1][j+1]);
                }
                if(j>0){
                    bst=min(bst,matrix[i+1][j-1]);
                }
                matrix[i][j]+=bst;
            }
        }
        for(int j=0;j<len;++j){
            ans=min(ans,matrix[0][j]);
        }
        return ans;
    }
};

三角形最小路径和

  1. 三角形最小路径和 - 力扣(LeetCode)
    https://leetcode-cn.com/problems/triangle/
  • 自底向上
class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        int ans=INT_MAX;
        for(int i=triangle.size()-2;i>=0;--i){
            for(int j=0;j<triangle[i].size();++j){
                int best=min(triangle[i+1][j],triangle[i+1][j+1]);
                triangle[i][j]+=best;
            }
        }
        for(int i=0;i<triangle[0].size();++i){
            ans=min(ans,triangle[0][i]);
        }
        return ans;
    }
};

不同路径

  1. 不同路径 - 力扣(LeetCode)
    https://leetcode-cn.com/problems/unique-paths/
class Solution {
public:
    int uniquePaths(int m, int n) {
        int dp[m+1][n+1];
        for(int i=0;i<m;++i){
            dp[i][0]=1;
        }
        for(int j=0;j<n;++j){
            dp[0][j]=1;
        }
        for(int i=1;i<m;++i){
            for(int j=1;j<n;++j){
                dp[i][j]=dp[i-1][j]+dp[i][j-1];
            }
        }
        return dp[m-1][n-1];
    }
};

不同路径 II

  1. 不同路径 II - 力扣(LeetCode)
    https://leetcode-cn.com/problems/unique-paths-ii/
class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m=obstacleGrid.size(),n=obstacleGrid[0].size();
        vector<vector<int>> dp(m+1,vector<int>(n+1));
        for(int i=0;i<m;++i){
            if(obstacleGrid[i][0]!=1){
                dp[i][0]=1;           
            }else{
                break;
            }
        }
        for(int j=0;j<n;++j){
            if(obstacleGrid[0][j]!=1){
                dp[0][j]=1;           
            }else{
                break;
            }
        }
        for(int i=1;i<m;++i){
            for(int j=1;j<n;++j){
                if(obstacleGrid[i][j]==1){
                    dp[i][j]=0;
                    // cout<<dp[i][j]<<" ";
                    continue;
                }
                dp[i][j]=dp[i-1][j]+dp[i][j-1];
                // cout<<dp[i][j]<<" ";
            }
            // cout<<endl;
        }
        return dp[m-1][n-1];
    }
};

最小路径和

  1. 最小路径和 - 力扣(LeetCode)
    https://leetcode-cn.com/problems/minimum-path-sum/
class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        int m=grid.size();
        int n=grid[0].size();
        for(int i=1;i<m;++i){
            grid[i][0]+=grid[i-1][0];
        }
        for(int j=1;j<n;++j){
            grid[0][j]+=grid[0][j-1];
        }
        for(int i=1;i<m;++i){
            for(int j=1;j<n;++j){
                grid[i][j]+=min(grid[i-1][j],grid[i][j-1]);
            }
        }
        return grid[m-1][n-1];
    }
};

最大正方形

  1. 最大正方形 - 力扣(LeetCode)
    https://leetcode-cn.com/problems/maximal-square/
class Solution {
public:
    int maximalSquare(vector<vector<char>>& matrix) {
        int m=matrix.size();
        int n=matrix[0].size();
        int ans=0;
        vector<vector<int>> dp(m,vector<int>(n));
        for(int i=0;i<m;++i){
            for(int j=0;j<n;++j){
                dp[i][j]=matrix[i][j]-'0';
                if(dp[i][j]==1)ans=1;
            }
        }
        for(int i=1;i<m;++i){
            for(int j=1;j<n;++j){
                if(dp[i][j]==1){
                    int len_a=dp[i-1][j];
                    int len_b=dp[i][j-1];
                    int len_min=min(len_a,len_b);
                    if(len_min==0)dp[i][j]=1;
                    else dp[i][j]=(dp[i-len_min][j-len_min]>0?len_min+1:len_min);
                    ans=max(dp[i][j],ans);
                }
                // cout<<dp[i][j]<<" ";
            }
            // cout<<endl;
        }
        return ans*ans;
    }
};

编辑距离

计算字符串的距离

计算字符串的距离_牛客题霸_牛客网
https://www.nowcoder.com/practice/3959837097c7413a961a135d7104c314?tpId=37&&tqId=21275&rp=1&ru=/ta/huawei&qru=/ta/huawei/question-ranking

#include<bits/stdc++.h>
using namespace std;
//空间可以优化成O(n)
int main() {
    string s1, s2;
    while (cin >> s1 >> s2) {
        vector<vector<int>> dp(s1.size() + 1, vector<int>(s2.size() + 1, 0));
        for (int i = 1; i <= s2.length(); i++) dp[0][i] = i;
        for (int i = 1; i <= s1.length(); i++) dp[i][0] = i;
        for(int i=1;i<=s1.length();i++)
            for (int j = 1; j <= s2.length(); j++) {
                int min1 = min(dp[i - 1][j], dp[i][j - 1]) + 1;//加一个字符
                dp[i][j] = min((s1[i - 1] == s2[j - 1] ? 0 : 1) + dp[i - 1][j - 1], min1);//修改字符
            }
        cout << dp[s1.size()][s2.size()] << endl;
    }
}

前缀和

二维区域和检索

  1. 二维区域和检索 - 矩阵不可变 - 力扣(LeetCode)
    https://leetcode-cn.com/problems/range-sum-query-2d-immutable/
  • 暴力
class Solution {
public:
    vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {
        vector<vector<int>> ans(mat.size(),vector<int>(mat[0].size()));
        for(int i=0;i<mat.size();++i){
            for(int j=0;j<mat[0].size();++j){
                // cout<<"i="<<i<<",j="<<j<<endl;
                int tmp=0;
                for(int m=i-k;m<=i+k;++m){
                    for(int n=j-k;n<=j+k;++n){
                        // cout<<m<<" "<<n<<endl;
                        if(m>=0&&m<mat.size()&&n>=0&&n<mat[0].size()){
                            tmp+=mat[m][n];
                        }
                    }
                }
                ans[i][j]=tmp;
            }
        }
        return ans;
    }
};
  • 行前缀和
class Solution {
public:
    vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int K) {
        int m = mat.size(), n = mat[0].size();
        // 1. 计算行前缀和
        vector<vector<int>> rowPrefix(m, vector<int>(n + 1, 0));
        for (int i = 0;  i < m; ++i) {
            for (int j = 1; j <= n; ++j) {
                rowPrefix[i][j] = rowPrefix[i][j-1] + mat[i][j-1];
            }
        }
        // 2. 行前缀和加快 该行 [j-K, j+K] 列之和查询
        vector<vector<int>> ans(m, vector<int>(n, 0));
        for (int i = 0;  i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                // 由于 i-K < 0 或 i+K >= m,因此行最大有效区间为 [max(i-K, 0), min(i+K, m-1)]
                for (int r = max(i-K, 0); r <= min(i+K, m-1); ++r) {
                    // 由于 j-K < 0 或 j+K >= n,因此最大有效区间为 [max(j-K, 0), min(j+K, n-1)]
                    ans[i][j] += rowPrefix[r][min(j+K, n-1)+1] - rowPrefix[r][max(j-K, 0)];
                    // for (int c = j - K; c <= j + K; ++c) {
                    //     if (0 <= r && r < m && 0 <= c && c < n)  // 检验合法坐标
                    //         ans[i][j] += mat[r][c];
                    // }
                }
            }
        }
        return ans;
    }
};

作者:boille
链接:https://leetcode-cn.com/problems/matrix-block-sum/solution/you-qian-ru-shen-bao-li-xing-lie-qian-zh-5503/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 二维前缀和
class Solution {
public:
    vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int K) {
        int m = mat.size(), n = mat[0].size();
        vector<vector<int>> prefix(m + 1, vector<int>(n + 1, 0));
        for (int i = 1;  i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                prefix[i][j] = prefix[i-1][j] + prefix[i][j-1] - prefix[i-1][j-1] + mat[i-1][j-1];
            }
        }
        vector<vector<int>> ans(m, vector<int>(n, 0));
        for (int i = 0;  i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                // 计算左上角坐标 (row1, col1) 和右下角坐标 (row2, col2)
                int row1 = max(i - K, 0), col1 = max(j - K, 0);
                int row2 = min(i + K, m - 1), col2 = min(j + K, n - 1);
                ans[i][j] = prefix[row2+1][col2+1] - prefix[row2+1][col1] - prefix[row1][col2+1] + prefix[row1][col1];
            }
        }
        return ans;
    }
};

作者:boille
链接:https://leetcode-cn.com/problems/matrix-block-sum/solution/you-qian-ru-shen-bao-li-xing-lie-qian-zh-5503/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

二维区域和检索- 矩阵不可变

  1. 二维区域和检索 - 矩阵不可变 - 力扣(LeetCode)
    https://leetcode-cn.com/problems/range-sum-query-2d-immutable/submissions/
class NumMatrix {
private:
    vector<vector<int>> sum;
public:
    NumMatrix(vector<vector<int>>& matrix) {
        sum.resize(matrix.size()+1,vector<int>(matrix[0].size()+1));
        for(int i=1;i<=matrix.size();++i){
            for(int j=1;j<=matrix[0].size();++j){
                sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+matrix[i-1][j-1];
            }
        }
    }
    
    int sumRegion(int row1, int col1, int row2, int col2) {
        return sum[row2+1][col2+1]-sum[row1][col2+1]-sum[row2+1][col1]+sum[row1][col1];
    }
};

/**
 * Your NumMatrix object will be instantiated and called as such:
 * NumMatrix* obj = new NumMatrix(matrix);
 * int param_1 = obj->sumRegion(row1,col1,row2,col2);
 */
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值