力扣刷题之动态规划

这篇博客深入探讨了多种算法问题的解决方案,包括动态规划在计算不同运算表达式优先级、求解丑数及其变种、计算比特位和不同数字个数以及找寻最大整除子集等问题上的应用。还涉及猜数字大小的优化策略。通过这些实例,展示了动态规划和递归等算法思想在解决复杂问题时的有效性。
摘要由CSDN通过智能技术生成

241.为运算表达式设计优先级

在这里插入图片描述
分治+递归
在这里插入图片描述

class Solution {
public:
    vector<int> diffWaysToCompute(string expression) {
        // 存储中间值
        vector<int> count;
        for(int i = 0; i < expression.size(); i ++) {
            char c = expression[i];
            // 找到运算符
            if(c == '+' || c == '-' || c == '*') {
                // 运算符左边的运算结果
                vector<int> left = diffWaysToCompute(expression.substr(0, i));
                // 运算符右边的运算结果
                vector<int> right = diffWaysToCompute(expression.substr(i + 1));
                // 左右两边继续运算
                for(int& l : left) {
                    for(int& r : right) {
                        switch(c) {
                            case '+':
                                count.push_back(l + r);
                                break;
                            case '-':
                                count.push_back(l - r);
                                break;
                            case '*':
                                count.push_back(l * r);    
                                break;
                        }
                    }
                }
            }
        }
        // count为空说明当前无运算符,只是单独的数字,直接放入count中
        if(count.size() == 0) {
            count.push_back(stoi(expression));
        }
        return count;
    }
};

263.丑数

在这里插入图片描述

class Solution {
public:
    bool isUgly(int n) {
        if (n <= 0) {
            return false;
        }
        vector<int> factors = {2, 3, 5};
        for (int factor : factors) {
            while (n % factor == 0) {
                n /= factor;
            }
        }
        return n == 1;
    }
};

264.丑数II

在这里插入图片描述
法一:最小堆+set
在这里插入图片描述

class Solution {
public:
    int nthUglyNumber(int n) {
        vector<int> factors = {2, 3, 5};
        unordered_set<long> seen;
        priority_queue<long, vector<long>, greater<long>> heap;
        seen.insert(1L);
        heap.push(1L);
        int ugly = 0;
        for (int i = 0; i < n; i++) {
            long curr = heap.top();
            heap.pop();
            ugly = (int)curr;
            for (int factor : factors) {
                long next = curr * factor;
                if (!seen.count(next)) {
                    seen.insert(next);
                    heap.push(next);
                }
            }
        }
        return ugly;
    }
};

313.超级丑数

在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    int nthSuperUglyNumber(int n, vector<int>& primes) {
        vector<int> dp(n + 1);  //用来存储丑数序列
        dp[1] = 1;          //第一个丑数是1
        int m = primes.size();
        vector<int> nums(m);     //记录新丑数序列
        vector<int> pointers(m, 1); //记录质数该与哪一位丑数做乘积
        for (int i = 2; i <= n; i++) {
            int minn = INT_MAX;
            for (int j = 0; j < m; j++) {
                nums[j] = dp[pointers[j]] * primes[j]; //旧丑数 * 质数序列 = 新丑数序列
                minn = min(minn, nums[j]);  //寻找所有新丑数中最小的丑数
            }
            dp[i] = minn;
            for (int j = 0; j < m; j++)
                if (minn == nums[j])        //如果此位置已经诞生过最小丑数
                    pointers[j]++;          //让此位置所取旧丑数向后推一位
        }
        return dp[n];
    }
};

338.比特位计数

在这里插入图片描述
法一:
在这里插入图片描述

class Solution {
public:
    int countOnes(int x) {
        int ones = 0;
        while (x > 0) {
            x &= (x - 1);
            ones++;
        }
        return ones;
    }

    vector<int> countBits(int n) {
        vector<int> bits(n + 1);
        for (int i = 0; i <= n; i++) {
            bits[i] = countOnes(i);
        }
        return bits;
    }
};

法二:动态规划
在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    vector<int> countBits(int num) {
        // 预先开好空间
        vector<int> dp(num + 1);

        // 同样不需要从0开始,因为dp[0] = 0
        for(int i = 1; i <= num; i++){
            if(i % 2 == 0){ // 偶数
                dp[i] = dp[i / 2];
            }
            else{ // 奇数
                dp[i] = dp[(i - 1) / 2] + 1;
            }
        }
        
        // dp数组即为所求
        return dp;
    }
};

357.计算各个位数不同的数字个数

在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    int countNumbersWithUniqueDigits(int n) {
        vector<int> dp(n+1);
        for(int i = 2; i <= n; ++i)
            dp[i] = dp[i-1]*10 + (9*pow(10, i-2) - dp[i-1])*(i-1);
        int sum = 0;
        for(auto& x : dp)
            sum += x;
        return pow(10, n) - sum;
    }
};

368.最大整除子集(序列DP问题)(经典)

在这里插入图片描述
与300题 最长上升子序列思想类似
在这里插入图片描述

https://leetcode-cn.com/problems/largest-divisible-subset/solution/gong-shui-san-xie-noxiang-xin-ke-xue-xi-0a3jc/

class Solution {
public:
    vector<int> largestDivisibleSubset(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        vector<int> f(n, 0);
        vector<int> g(n ,0);
        
        for(int i = 0; i < n; i++) {
            // 至少包含自身一个数,因此起始长度为 1,由自身转移而来
            int len = 1, prev = i;
            for(int j = 0; j < i; j++) {
                if(nums[i] % nums[j] == 0) {
                    // 如果能接在更长的序列后面,则更新「最大长度」&「从何转移而来」
                    if(f[j] + 1 > len) {
                        len = f[j] + 1;
                        prev = j;
                    }
                }
            }
            f[i] = len;
            g[i] = prev;
        }

        // 遍历所有的 f[i],取得「最大长度」和「对应下标」
        int idx = max_element(f.begin(), f.end()) - f.begin();
        int max = f[idx];

        // 使用 g[] 数组回溯出具体方案
        vector<int> ans;
        while(ans.size() != max) {
            ans.push_back(nums[idx]);
            idx = g[idx];
        }
        return ans;
    }
};

375.猜数字大小II

在这里插入图片描述

https://leetcode-cn.com/problems/guess-number-higher-or-lower-ii/solution/dong-tai-gui-hua-c-you-tu-jie-by-zhang-xiao-tong-2/

在这里插入图片描述

class Solution {
public:
    int getMoneyAmount(int n) {
        if(n==1)
            return 0;
        //定义矩阵
        int dp[n+1][n+1];
        //初始化“\”
        for(int i=0;i<=n;i++){
            for(int j=0;j<=n;j++){
                dp[i][j]=INT_MAX;
            }
        }
        //定义基础值dp[i][i]
        for(int i=0;i<=n;i++){
            dp[i][i]=0;
        }

        //按列来,从第2列开始
        for(int j=2;j<=n;j++){
            //按行来,从下往上
            for(int i=j-1;i>=1;i--){
                //算除了两端的每一个分割点
                for(int k=i+1;k<=j-1;k++){
                    dp[i][j]=min(k+max(dp[i][k-1],dp[k+1][j]),dp[i][j]);
                }
                //算两端
                dp[i][j]=min(dp[i][j],i+dp[i+1][j]);
                dp[i][j]=min(dp[i][j],j+dp[i][j-1]);
            }
        }
        return dp[1][n];
    }
};

300.最长上升子序列

在这里插入图片描述
法一:动态规划
在这里插入图片描述

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n = (int)nums.size();
        if (n == 0) {
            return 0;
        }
        vector<int> dp(n, 0);
        for (int i = 0; i < n; ++i) {
            dp[i] = 1;
            for (int j = 0; j < i; ++j) {
                if (nums[j] < nums[i]) {
                    dp[i] = max(dp[i], dp[j] + 1);
                }
            }
        }
        return *max_element(dp.begin(), dp.end());
    }
};

在这里插入图片描述
法二:贪心+二分查找(labuladong耐心排序)
在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int len = 1, n = (int)nums.size();
        if (n == 0) {
            return 0;
        }
        vector<int> d(n + 1, 0);
        d[len] = nums[0];
        for (int i = 1; i < n; ++i) {
            if (nums[i] > d[len]) {
                d[++len] = nums[i];
            } else {
                int l = 1, r = len, pos = 0; // 如果找不到说明所有的数都比 nums[i] 大,此时要更新 d[1],所以这里将 pos 设为 0
                while (l <= r) {
                    int mid = (l + r) >> 1;
                    if (d[mid] < nums[i]) {
                        pos = mid;
                        l = mid + 1;
                    } else {
                        r = mid - 1;
                    }
                }
                d[pos + 1] = nums[i];
            }
        }
        return len;
    }
};

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值