力扣11.3

1981. 最小化目标值与所选元素的差

给你一个大小为 m x n 的整数矩阵 mat 和一个整数 target

从矩阵的 每一行 中选择一个整数,你的目标是 最小化 所有选中元素之 和 与目标值 target 的 绝对差 。

返回 最小的绝对差 。

ab 两数字的 绝对差 是 a - b 的绝对值。

数据范围

  • m == mat.length
  • n == mat[i].length
  • 1 <= m, n <= 70
  • 1 <= mat[i][j] <= 70
  • 1 <= target <= 800

分析

分组背包,将被一行的元素看作一组,每次只能选一行中的一个元素,朴素dp做法是令 d p i j dpij dpij为选择前i行,元素之和为j的方案是否存在

状态转移为:

  • d p i j ∣ = d p i − 1 j − c dpij \ |= \ dpi-1j-c dpij ∣= dpi−1j−c,(c为每一行对应的元素)

最后的答案只需要遍历dpni即可

代码

c 复制代码
class Solution {
public:
    const static int N = 72;
    bool dp[N][5000];
    int minimizeTheDifference(vector<vector<int>>& mat, int target) {
        int n = mat.size(), m = mat[0].size();
        memset(dp, false, sizeof(dp));
        dp[0][0] = true;
        for(int i = 0; i < n; i ++ ) {
            for(int j = 0; j < 5000; j ++ ) {
                for(int k = 0; k < m; k ++ ) {
                    int c = mat[i][k];
                    if(j >= c) {
                        if(dp[i][j - c]) dp[i + 1][j] = true;
                    }
                }
            }
        }
        int res = 0x3f3f3f3f;
        for(int i = 0; i < 5000; i ++ ) {
            if(dp[n][i]) {
                if(abs(target - i) < abs(res - target)) {
                    res = i;
                }
            }
        }
        return abs(target - res);
    }
};

优化

可以使用bitset进行优化,bitset优化的原理是:

将dp数组转换成一个二进制数,若二进制数的第i位为1,则说明体积为j的方案存在

在滚动数组的过程中,我们以c=3为例

  • d p 3 ∣ = d p 0 dp3 \ |= \ dp0 dp3 ∣= dp0
  • d p 4 ∣ = d p 1 dp4 \ |= \ dp1 dp4 ∣= dp1
  • d p 5 ∣ = d p 2 dp5 \ |= \ dp2 dp5 ∣= dp2
  • ...

若转换成二进制数k,则是将低M-c位左移c位在或回去,即:

  • f ∣ = f < < c f \ |= f << c f ∣=f<<c

需要注意的是,每一行都必须选元素,因此只能使用上一轮的状态,需要两个dp合并使用,否则会受之前状态的影响

代码

c 复制代码
class Solution {
public:
    const static int N = 72, M = 5000;
    int minimizeTheDifference(vector<vector<int>>& mat, int target) {
        int n = mat.size(), m = mat[0].size();
        bitset<M> f1{1}, f2;
        for(int i = 0; i < n; i ++ ) {
            for(int k = 0; k < m; k ++ ) {
                int c = mat[i][k];
                f2 |= f1 << c;
            }
            f1 = f2;
            f2.reset();
        }
        int res = 0x3f3f3f3f;
        for(int i = 0; i < 5000; i ++ ) {
            if(f1.test(i)) {
                if(abs(target - i) < abs(res - target)) {
                    res = i;
                }
            }
        }
        return abs(target - res);
    }
};

1155. 掷骰子等于目标和的方法数

这里有 n 个一样的骰子,每个骰子上都有 k 个面,分别标号为 1k

给定三个整数 nktarget,请返回投掷骰子的所有可能得到的结果(共有 k^n 种方式),使得骰子面朝上的数字总和等于 target

由于答案可能很大,你需要对 109 + 7 取模。

数据范围

  • 1 <= n, k <= 30
  • 1 <= target <= 1000

分析

简单背包,注意每个骰子都必须使用

代码

c 复制代码
typedef long long LL;
class Solution {
public:
    const static int N = 35, M = 1005, mod = 1e9 + 7;
    LL dp[N][M];
    LL numRollsToTarget(int n, int k, int target) {
        dp[0][0] = 1;
        for(int i = 1; i <= n; i ++ ) {
            for(int j = 0; j <= target; j ++ ) {
                for(int z = 1; z <= k; z ++ ) {
                    if(j >= z) {
                        dp[i][j] += dp[i - 1][j - z] % mod;
                        dp[i][j] %= mod;
                    }
                }
            }
        }
        return dp[n][target];
    }
};

2585. 获得分数的方法数

考试中有 n 种类型的题目。给你一个整数 target 和一个下标从 0 开始的二维整数数组 types ,其中 types[i] = [counti, marksi] 表示第 i 种类型的题目有 counti 道,每道题目对应 marksi 分。

返回你在考试中恰好得到 target 分的方法数。由于答案可能很大,结果需要对 109 +7 取余。

注意,同类型题目无法区分。

比如说,如果有 3 道同类型题目,那么解答第 1 和第 2 道题目与解答第 1 和第 3 道题目或者第 2 和第 3 道题目是相同的。

数据范围

  • 1 <= target <= 1000
  • n == types.length
  • 1 <= n <= 50
  • types[i].length == 2
  • 1 <= counti, marksi <= 50

分析

多重背包

代码

c 复制代码
typedef long long LL;
class Solution {
public:
    const static int N = 55, M = 1005, mod = 1e9 + 7;
    LL dp[M];
    int waysToReachTarget(int target, vector<vector<int>>& types) {
        int n = types.size();
        dp[0] = 1;
        for(int i = 0; i < n; i ++ ) {
            int a = types[i][0], b = types[i][1];
            for(int j = target; j >= 0; j -- ) {
                for(int k = 1; k <= a; k ++ ) {
                    if(j >= k * b) {
                        dp[j] += dp[j - k * b];
                        dp[j] %= mod;
                    }
                }
            }
        }
        return dp[target];
    }
};
相关推荐
YouonlyliveonceC2 分钟前
《大话数据结构》第4章实战:中缀表达式转后缀 + 后缀表达式求值(完整可运行实现)
算法
phoenix@Capricornus11 分钟前
从统计决策到贝叶斯估计
人工智能·算法·机器学习
AICDragon20 分钟前
1.8%就够了:K3的896个MoE专家,为什么激活率这么低反而是好事?
人工智能·算法
VALENIAN瓦伦尼安教学设备39 分钟前
ASHOOTER激光对中仪如何通过颜色确定调整是否合适
数据库·嵌入式硬件·算法
货拉拉技术40 分钟前
重塑 Agent 度量衡:基于 LLM-as-a-Judge 的离线评估体系与实践
算法·设计模式
车压1 小时前
从“为什么”理解注意力机制
算法
wabs6661 小时前
关于哈希表【力扣15.三数之和的思考】
数据结构·算法·leetcode·散列表·哈希表·三数之和
退休倒计时1 小时前
【每日一题】LeetCode 88. 合并两个有序数组 TypeScript
算法·leetcode·职场和发展·typescript
键盘会跳舞1 小时前
C++:函数对象与 std::function 源码级深度拆解——泛型算法的策略内核与可调用对象统一封装
c++·算法·仿函数
手写码匠1 小时前
华为云Flexus+DeepSeek征文|Agent 评测体系实战:用 DeepSeek-R1 当裁判,打造 Dify Agent 的自动化回归测试流水线
人工智能·深度学习·算法·aigc