当前位置: 首页 > news >正文

力扣刷题DAY8(动态规划)

一、01背包问题变式——子集和问题

目标和

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = reduce(nums.begin(), nums.end());
        int s = sum + target;
        if (s % 2 != 0 || s < 0)
            return 0;
        else {
            int t = s / 2;
            int n = nums.size();
            vector f(n + 1, vector<int>(t + 1));
            f[0][0] = 1;
            for (int i = 1; i <= n; i++) {
                for (int j = 0; j <= t; j++) {
                    if (nums[i - 1] > j)
                        f[i][j] = f[i - 1][j];
                    else
                        f[i][j] = f[i - 1][j] + f[i-1][j - nums[i - 1]];
                }
            }
            return f[n][t];
        }
    }
};

复杂度分析

  • 时间复杂度:O(nt),其中 n 为 nums 的长度,t 为 nums 的元素和减去 target 的绝对值。
  • 空间复杂度:O(nt)。保存多少状态,就需要多少空间。

思路

【视频】教你一步步思考动态规划!一个视频讲透!(Python/Java/C++/Go)https://leetcode.cn/problems/target-sum/solutions/2119041/jiao-ni-yi-bu-bu-si-kao-dong-tai-gui-hua-s1cx

解释状态转移方程

01背包和子集和的关键区别

        正是这个区别,导致了二者的状态转移方程不一样,初始化也不一样(01背包f[0][0]是0,而子集和是1)。

优化版(变成一维):

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = reduce(nums.begin(), nums.end()); // 计算 nums 的和
        int s = sum + target;

        // 边界条件检查
        if (s < 0 || s % 2 != 0) {
            return 0;
        }

        int t = s / 2; // 目标和
        int n = nums.size();

        // 初始化动态规划数组
        vector<int> f(t + 1, 0);
        f[0] = 1;

        // 动态规划求解
        for (int i = 1; i <= n; i++) {
            for (int j = t; j >= 0; j--) { // j 从 0 开始
                if (nums[i - 1] > j) {
                    f[j] = f[j]; // 不能选择当前元素
                } else {
                    f[j] = f[j] + f[j - nums[i - 1]]; // 选择或不选择当前元素
                }
            }
        }

        return f[t]; // 返回结果
    }
};

复杂度分析

  • 时间复杂度:O(nt),其中 n 为 nums 的长度,t 为 nums 的元素和减去 target 的绝对值。
  • 空间复杂度:O(t)。二维变一维。

二、完全背包问题

零钱兑换

这道题相当于让每个硬币的权重都为1,选取面值和为target的权重最小的完全背包问题。

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        int n = coins.size();
        vector f(n + 1, vector<int>(amount + 1, INT_MAX / 2)); // 除 2 防止下面 + 1 溢出
        f[0][0] = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= amount; j++) {
                f[i][j] = f[i - 1][j];
                if (coins[i - 1] <= j)
                    f[i][j] = min(f[i][j], f[i][j - coins[i - 1]] + 1);
            }
        }
        int ans = f[n][amount];
        return ans < INT_MAX / 2 ? ans : -1;
    }
};

复杂度分析

  • 时间复杂度:O(n⋅amount),其中 n 为 coins 的长度。
  • 空间复杂度:O(n⋅amount)。

初始化问题

 分别考虑这三种特殊情况即可。

 为什么是恰好等于amount,而不是小于等于?

优化版(变成一维)

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        int n = coins.size();
        vector<int> f(amount + 1, INT_MAX / 2); // 除 2 防止下面 + 1 溢出
        f[0] = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= amount; j++) {
                f[j] = f[j];
                if (coins[i - 1] <= j)
                    f[j] = min(f[j], f[j - coins[i - 1]] + 1);
            }
        }
        int ans = f[amount];
        return ans < INT_MAX / 2 ? ans : -1;
    }
};

复杂度分析

  • 时间复杂度:O(n⋅amount),其中 n 为 coins 的长度。
  • 空间复杂度:O(amount)。

类似题目:

完全平方数

class Solution {
public:
    int numSquares(int n) {
        // 动态生成完全平方数
        vector<int> wan;
        for (int i = 1; i * i <= n; i++) {
            wan.push_back(i * i);
        }
        int m = wan.size();
        vector<vector<int>> f(m + 1, vector<int>(n + 1, INT_MAX / 2));  // 初始化 f 数组
        f[0][0] = 0;  // 凑成金额 0 需要 0 个完全平方数

        // 动态规划求解
        for (int i = 1; i <= m; i++) {
            for (int j = 0; j <= n; j++) {  // j 从 0 开始
                f[i][j] = f[i - 1][j];  // 不选择当前完全平方数
                if (wan[i - 1] <= j) {
                    f[i][j] = min(f[i][j], f[i][j - wan[i - 1]] + 1);  // 选择当前完全平方数
                }
            }
        }

        // 返回结果
        int ans = f[m][n];
        return ans < INT_MAX / 2 ? ans : -1;
    }
};

复杂度分析

  • 时间复杂度:O(N∗Sqrt(N))
  • 空间复杂度:O(N∗Sqrt(N))

优化版:

class Solution {
public:
    int numSquares(int n) {
        // 动态生成完全平方数
        vector<int> wan;
        for (int i = 1; i * i <= n; i++) {
            wan.push_back(i * i);
        }
        int m = wan.size();
        vector<int> f(n + 1, INT_MAX / 2);
        f[0] = 0; // 凑成金额 0 需要 0 个完全平方数

        // 动态规划求解
        for (int i = 1; i <= m; i++) {
            for (int j = 0; j <= n; j++) { // j 从 0 开始
                f[j] = f[j];               // 不选择当前完全平方数
                if (wan[i - 1] <= j) {
                    f[j] =
                        min(f[j], f[j - wan[i - 1]] + 1); // 选择当前完全平方数
                }
            }
        }

        // 返回结果
        int ans = f[n];
        return ans < INT_MAX / 2 ? ans : -1;
    }
};

相关文章:

  • C/C++实现显微镜玻片球状细胞识别与计数
  • 计算机组成原理(第三章 存储系统)
  • 【自学笔记】R语言基础知识点总览-持续更新
  • 爬虫案例六用协程爬取趣笔阁
  • 13.【线性代数】——复习课
  • MyBatis增删改查:静态与动态SQL语句拼接及SQL注入问题解析
  • 如何选择开源向量数据库
  • XPath 语法无法定位到 svg 标签
  • Vue源码解析之mustache模板引擎
  • nodejs express设置允许跨域示例
  • C#运算符详解
  • 【免费】2013-2019年上市公司知识产权数据
  • 【架构艺术】Go语言微服务monorepo的代码架构设计
  • C、C++读取空格、回车符函数【getline、cin.get、cin.getline、std::noskipws】
  • 仿muduo库实现高并发服务器-面试常见问题
  • C#核心(22)string
  • 从0开始完成基于异步服务器的boost搜索引擎
  • 可重构智能表面(RIS)的全面介绍
  • 渐进稀疏注意力PSA详解及代码复现
  • KMP 算法的 C 语言实现
  • 2016广州网站设计公司/网站模板下载
  • 网站建设第一品牌/资阳市网站seo
  • 嘉兴网站建设哪家好/网络广告投放
  • wordpress学生/广东做seo的公司
  • 浙江网站建设/新浪网今日乌鲁木齐新闻
  • 什么网站做批发零食的很多/一站式营销推广