Leetcode3276. 选择矩阵中单元格的最大得分

Every day a Leetcode

题目来源:3276. 选择矩阵中单元格的最大得分

解法1:回溯

每一行最多选1个数字,如果要选,就要保证前面没有选择过该数字,然后将得分累加,传入下一次递归,如果不选,那就是直接跳过当前这一行,得分不累加。

代码:

/*
 * @lc app=leetcode.cn id=3276 lang=cpp
 *
 * [3276] 选择矩阵中单元格的最大得分
 */

// @lc code=start
class Solution
{
private:
    int m, n;
    int ans = 0;

public:
    int maxScore(vector<vector<int>> &grid)
    {
        m = grid.size(), n = m ? grid[0].size() : 0;
        unordered_set<int> s;
        backtrace(0, 0, s, grid);
        return ans;
    }
    // 辅函数 - 回溯
    void backtrace(int level, int score, unordered_set<int> s, vector<vector<int>> &grid)
    {
        if (level == m)
        {
            ans = max(ans, score);
            return;
        }
        for (int j = 0; j < n; j++)
        {
            if (s.count(grid[level][j]) == 0)
            {
                s.insert(grid[level][j]);
                score += grid[level][j];
                backtrace(level + 1, score, s, grid);
                score -= grid[level][j];
                s.erase(grid[level][j]);
                backtrace(level + 1, score, s, grid);
            }
        }
    }
};
// @lc code=end

结果:超时

在这里插入图片描述

复杂度分析:

时间复杂度:O(m * n * 2m*n),其中 m 和 n 分别为二维矩阵 grid 的行数和列数。

空间复杂度:O(m * n + 2m),其中 m 和 n 分别为二维矩阵 grid 的行数和列数。

解法2:状压 DP

定义状态为 dp[i][j],表示在 [1,i] 中选数字,所选数字所处的行号不能在集合 j 中,每行至多一个数且没有相同元素时,所选元素之和的最大值。

讨论元素 i 选或不选:

  • 不选 i,问题变成在 [1,i−1] 中选数字,所选数字所处的行号不能在集合 j 中,每行至多一个数且没有相同元素时,所选元素之和的最大值,即 dp[i-1][j]。
  • 选 i,枚举选第 k 行的 i(提前预处理 i 所处的行号),问题变成在 [1,i−1] 中选数字(注意 i 只能选一次),所选数字所处的行号不能在集合 j∪{k} 中,每行至多一个数且没有相同元素时,所选元素之和的最大值,即 dp[i-1][j∪{k}]。

两种情况取最大值。

代码:

// 状压 DP

class Solution
{
public:
    int maxScore(vector<vector<int>> &grid)
    {
        int m = grid.size();
        unordered_map<int, int> pos;
        for (int i = 0; i < m; i++)
        {
            for (int &x : grid[i])
            {
                // 保存所有包含 x 的行号(压缩成二进制数)
                pos[x] |= 1 << i;
            }
        }

        vector<int> all_nums;
        for (auto &[x, _] : pos)
        {
            all_nums.push_back(x);
        }

        int u = 1 << m;
        vector<vector<int>> dp(all_nums.size() + 1, vector<int>(u));
        for (int i = 0; i < all_nums.size(); i++)
        {
            int x = all_nums[i];
            for (int j = 0; j < u; j++)
            {
                dp[i + 1][j] = dp[i][j]; // 不选 x
                for (int t = pos[x], lb; t; t ^= lb)
                {
                    lb = t & -t;       // lb = 1<<k,其中 k 是行号
                    if ((j & lb) == 0) // 没选过第 k 行的数
                    {
                        dp[i + 1][j] = max(dp[i + 1][j], dp[i][j | lb] + x); // 选第 k 行的 x
                    }
                }
            }
        }
        return dp.back()[0];
    }
};

结果:

在这里插入图片描述

复杂度分析:

时间复杂度:O(m * n * 2m),其中 m 和 n 分别为二维矩阵 grid 的行数和列数。

空间复杂度:O(m * n + 2m),其中 m 和 n 分别为二维矩阵 grid 的行数和列数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值