【每日一题Day246】LC1659最大化网格幸福感 | 状压dp

*最大化网格幸福感【LC1659】

给你四个整数 mnintrovertsCountextrovertsCount 。有一个 m x n 网格,和两种类型的人:内向的人和外向的人。总共有 introvertsCount 个内向的人和 extrovertsCount 个外向的人。

请你决定网格中应当居住多少人,并为每个人分配一个网格单元。 注意,不必 让所有人都生活在网格中。

每个人的 幸福感 计算如下:

  • 内向的人 开始 时有 120 个幸福感,但每存在一个邻居(内向的或外向的)他都会 失去 30 个幸福感。
  • 外向的人 开始 时有 40 个幸福感,每存在一个邻居(内向的或外向的)他都会 得到 20 个幸福感。

邻居是指居住在一个人所在单元的上、下、左、右四个直接相邻的单元中的其他人。

网格幸福感 是每个人幸福感的 总和 。 返回 最大可能的网格幸福感

好难 了解一下

状压dp+按行

  • 思路

    朴素的dfs会超时,由于每个位置只有「空」「内向」「外向」三种状态,因此可以将这三个状态分别编码为 0,1,2,并用一个长度(位数)为 n 的三进制数对一行的状态进行编码,用状压dp解决。

    • 子问题:如果按行处理的话,枚举每一行的状态得到的最大分数,每一行的分数分为「行内分数」和「行外分数」

      • 「行内分数」:在同一行内每个人的分数,内向 120分,外向 40分;以及由于两人相邻(在同一行内,即左右相邻)贡献的额外分数,两个内向 −60分,两个外向 40 分,其余情况 −10分;

      • 「行外分数」:由于两人相邻(在不同行内,即上下相邻)贡献的额外分数,同理为 −60,40,−10分中的一种。

      • 递归的过程中有四个变量:当前选择的行、前一行的状态码、可供选择的内向的人和外向的人的个数,因此在记忆化需要用四维memo记录

    • 递归函数定义:

      f(maskL,rowf(\textit{mask}_L, \textit{row}f(maskL,row, 表示当我们枚举到第 rowrowrow行且之前的行已经枚举完成,第 row−1\textit{row}-1row1行的状态为 mask\textit{mask}mask ,还剩余 nxnxnx个内向的人以及 wx\textit{wx}wx 个外向的人的情况下,从第 rowrowrow 行开始往后的所有行可以获得的最大分数。

    • 状态转移

      枚举第 row\textit{row}row 行的状态 mask\textit{mask}mask,计算「行内分数」以及「行外分数」,并扣除该行分别使用的内向和外向的人数,再转移到下一行。
      KaTeX parse error: Expected 'EOF', got '&' at position 2: &̲ f(mask_L,row,n…

      • countnx(mask)\text{count}_\textit{nx}(mask)countnx(mask)表示状态 maskmaskmask 中有多少个内向的人,即有多少个 1;
      • countwx(mask)\text{count}_\textit{wx} (mask)countwx(mask) 表示状态mask\textit{mask}mask中有多少个外向的人,即有多少个 2;
      • scoreinner(mask)\text{score}_\textit{inner} (mask)scoreinner(mask) 表示状态 maskmaskmaskmask\textit{mask}maskmaskmaskmask 的「行内分数」;
      • scoreouter(mask,maskL\text{score}_\textit{outer}(\textit{mask}, \textit{mask}_Lscoreouter(mask,maskL ) 表示状态 mask\textit{mask}maskmaskL\textit{mask}_LmaskL 之间的「行外分数」。
    • 递归边界

      • 剩余可以选择的人数为0时,返回0
      • row=mrow=mrow=m时,返回0
  • 实现

    class Solution {
        private int m;
        private int mx;
        private int[] f;
        private int[][] g;// 记录每个位置的消息
        private int[][] bits;
        private int[] ix;
        private int[] ex;
        private Integer[][][][] memo;
        private final int[][] h = {{0, 0, 0}, {0, -60, -10}, {0, -10, 40}};
    
        public int getMaxGridHappiness(int m, int n, int introvertsCount, int extrovertsCount) {
            this.m = m;
            mx = (int) Math.pow(3, n);// 三进制枚举的最大值
            f = new int[mx];// 行内分数
            g = new int[mx][mx];// 行间分数
            bits = new int[mx][n];// mask中第i列的比特值
            ix = new int[mx];// mask中内向人的个数
            ex = new int[mx];// mask中外向人的个数
            memo = new Integer[m][mx][introvertsCount + 1][extrovertsCount + 1];
            for (int i = 0; i < mx; ++i) {
                int mask = i;
                for (int j = 0; j < n; ++j) {
                    int x = mask % 3;// 状态值
                    mask /= 3;
                    bits[i][j] = x;
                    if (x == 1) {
                        ix[i]++;
                        f[i] += 120;
                    } else if (x == 2) {
                        ex[i]++;
                        f[i] += 40;
                    }
                    if (j > 0) {
                        f[i] += h[x][bits[i][j - 1]];
                    }
                }
            }
            for (int i = 0; i < mx; ++i) {
                for (int j = 0; j < mx; ++j) {
                    for (int k = 0; k < n; ++k) {
                        g[i][j] += h[bits[i][k]][bits[j][k]];
                    }
                }
            }
            return dfs(0, 0, introvertsCount, extrovertsCount);
        }
    
        private int dfs(int i, int pre, int ic, int ec) {
            if (i == m || (ic == 0 && ec == 0)) {
                return 0;
            }
            if (memo[i][pre][ic][ec] != null) {
                return memo[i][pre][ic][ec];
            }
            int ans = 0;
            for (int cur = 0; cur < mx; ++cur) {
                if (ix[cur] <= ic && ex[cur] <= ec) {
                    ans = Math.max(ans, f[cur] + g[pre][cur] + dfs(i + 1, cur, ic - ix[cur], ec - ex[cur]));
                }
            }
            return memo[i][pre][ic][ec] = ans;
        }
    }
    
    作者:ylb
    链接:https://leetcode.cn/problems/maximize-grid-happiness/solutions/2318399/python3javacgotypescript-yi-ti-shuang-ji-fssp/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    

轮廓线dp

https://leetcode.cn/problems/maximize-grid-happiness/solutions/2318399/python3javacgotypescript-yi-ti-shuang-ji-fssp/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值