Skip to content

1659. Maximize Grid Happiness 👍

  • Time: $O(mn \cdot 2^m2^n)$
  • Space: $O(mn \cdot 2^m2^n)$
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
class Solution {
 public:
  int getMaxGridHappiness(int m, int n, int introvertsCount,
                          int extrovertsCount) {
    const int twoToThePowerOfN = pow(2, n);
    vector<vector<vector<vector<vector<int>>>>> mem(
        m * n, vector<vector<vector<vector<int>>>>(
                   twoToThePowerOfN,
                   vector<vector<vector<int>>>(
                       twoToThePowerOfN,
                       vector<vector<int>>(introvertsCount + 1,
                                           vector<int>(extrovertsCount + 1)))));
    return getMaxGridHappiness(m, n, 0, 0, 0, introvertsCount, extrovertsCount,
                               mem);
  }

 private:
  // Calculates the cost based on left and up neighbors.
  //
  // The `diff` parameter represents the happiness change due to the current
  // placed person in (i, j). We add `diff` each time we encounter a neighbor
  // (left or up) who is already placed.
  //
  // 1. If the neighbor is an introvert, we subtract 30 from cost.
  // 2. If the neighbor is an extrovert, we add 20 to from cost.
  int getPlacementCost(int n, int i, int j, int inMask, int exMask, int diff) {
    int cost = 0;
    if (i > 0) {
      if ((1 << (n - 1)) & inMask)
        cost += diff - 30;
      if ((1 << (n - 1)) & exMask)
        cost += diff + 20;
    }
    if (j > 0) {
      if (1 & inMask)
        cost += diff - 30;
      if (1 & exMask)
        cost += diff + 20;
    }
    return cost;
  }

  int getMaxGridHappiness(int m, int n, int pos, int inMask, int exMask,
                          int inCount, int exCount,
                          vector<vector<vector<vector<vector<int>>>>>& mem) {
    // `inMask` is the placement of introvert people in the last n cells.
    // e.g. if we have m = 2, n = 3, i = 1, j = 1, then inMask = 0b101 means
    //
    // ? 1 0
    // 1 x ? (x := current position)
    const int i = pos / n;
    const int j = pos % n;
    if (i == m)
      return 0;
    if (mem[pos][inMask][exMask][inCount][exCount] > 0)
      return mem[pos][inMask][exMask][inCount][exCount];

    const int shiftedInMask = (inMask << 1) & ((1 << n) - 1);
    const int shiftedExMask = (exMask << 1) & ((1 << n) - 1);

    const int skip = getMaxGridHappiness(m, n, pos + 1, shiftedInMask,
                                         shiftedExMask, inCount, exCount, mem);
    const int placeIntrovert =
        inCount > 0
            ? 120 + getPlacementCost(n, i, j, inMask, exMask, -30) +
                  getMaxGridHappiness(m, n, pos + 1, shiftedInMask | 1,
                                      shiftedExMask, inCount - 1, exCount, mem)
            : INT_MIN;
    const int placeExtrovert =
        exCount > 0 ? 40 + getPlacementCost(n, i, j, inMask, exMask, 20) +
                          getMaxGridHappiness(m, n, pos + 1, shiftedInMask,
                                              shiftedExMask | 1, inCount,
                                              exCount - 1, mem)
                    : INT_MIN;
    return mem[pos][inMask][exMask][inCount][exCount] =
               max({skip, placeIntrovert, placeExtrovert});
  }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class Solution {
  public int getMaxGridHappiness(int m, int n, int introvertsCount, int extrovertsCount) {
    final int twoToThePowerOfN = (int) Math.pow(2, n);
    int[][][][][] mem = new int[m * n][twoToThePowerOfN][twoToThePowerOfN][introvertsCount + 1]
                               [extrovertsCount + 1];
    return getMaxGridHappiness(m, n, 0, 0, 0, introvertsCount, extrovertsCount, mem);
  }

  // Calculates the cost based on left and up neighbors.
  //
  // The `diff` parameter represents the happiness change due to the current
  // placed person in (i, j). We add `diff` each time we encounter a neighbor
  // (left or up) who is already placed.
  //
  // 1. If the neighbor is an introvert, we subtract 30 from cost.
  // 2. If the neighbor is an extrovert, we add 20 to from cost.
  private int getPlacementCost(int n, int i, int j, int inMask, int exMask, int diff) {
    int cost = 0;
    if (i > 0) {
      if (((1 << (n - 1)) & inMask) > 0)
        cost += diff - 30;
      if (((1 << (n - 1)) & exMask) > 0)
        cost += diff + 20;
    }
    if (j > 0) {
      if ((1 & inMask) > 0)
        cost += diff - 30;
      if ((1 & exMask) > 0)
        cost += diff + 20;
    }
    return cost;
  }

  private int getMaxGridHappiness(int m, int n, int pos, int inMask, int exMask, int inCount,
                                  int exCount, int[][][][][] mem) {
    // `inMask` is the placement of introvert people in the last n cells.
    // e.g. if we have m = 2, n = 3, i = 1, j = 1, then inMask = 0b101 means
    //
    // ? 1 0
    // 1 x ? (x := current position)
    final int i = pos / n;
    final int j = pos % n;
    if (i == m)
      return 0;
    if (mem[pos][inMask][exMask][inCount][exCount] > 0)
      return mem[pos][inMask][exMask][inCount][exCount];

    final int shiftedInMask = (inMask << 1) & ((1 << n) - 1);
    final int shiftedExMask = (exMask << 1) & ((1 << n) - 1);

    final int skip =
        getMaxGridHappiness(m, n, pos + 1, shiftedInMask, shiftedExMask, inCount, exCount, mem);
    final int placeIntrovert =
        inCount > 0 ? 120 + getPlacementCost(n, i, j, inMask, exMask, -30) +
                          getMaxGridHappiness(m, n, pos + 1, shiftedInMask | 1, shiftedExMask,
                                              inCount - 1, exCount, mem)
                    : Integer.MIN_VALUE;
    final int placeExtrovert =
        exCount > 0 ? 40 + getPlacementCost(n, i, j, inMask, exMask, 20) +
                          getMaxGridHappiness(m, n, pos + 1, shiftedInMask, shiftedExMask | 1,
                                              inCount, exCount - 1, mem)
                    : Integer.MIN_VALUE;
    return mem[pos][inMask][exMask][inCount][exCount] =
               Math.max(skip, Math.max(placeIntrovert, placeExtrovert));
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
class Solution:
  def getMaxGridHappiness(
      self,
      m: int,
      n: int,
      introvertsCount: int,
      extrovertsCount: int,
  ) -> int:
    def getPlacementCost(
        i: int,
        j: int,
        inMask: int,
        exMask: int,
        diff: int,
    ) -> int:
      """Calculates the cost based on left and up neighbors.

      The `diff` parameter represents the happiness change due to the current
      placed person in (i, j). We add `diff` each time we encounter a neighbor
      (left or up) who is already placed.

      1. If the neighbor is an introvert, we subtract 30 from cost.
      2. If the neighbor is an extrovert, we add 20 to from cost.
      """
      cost = 0
      if i > 0:
        if (1 << (n - 1)) & inMask:
          cost += diff - 30
        if (1 << (n - 1)) & exMask:
          cost += diff + 20
      if j > 0:
        if 1 & inMask:
          cost += diff - 30
        if 1 & exMask:
          cost += diff + 20
      return cost

    @functools.lru_cache(None)
    def dp(
        pos: int, inMask: int, exMask: int, inCount: int, exCount: int
    ) -> int:
      # `inMask` is the placement of introvert people in the last n cells.
      # e.g. if we have m = 2, n = 3, i = 1, j = 1, then inMask = 0b101 means
      #
      # ? 1 0
      # 1 x ? (x := current position)
      i, j = divmod(pos, n)
      if i == m:
        return 0

      shiftedInMask = (inMask << 1) & ((1 << n) - 1)
      shiftedExMask = (exMask << 1) & ((1 << n) - 1)

      skip = dp(pos + 1, shiftedInMask, shiftedExMask, inCount, exCount)
      placeIntrovert = (
          120 + getPlacementCost(i, j, inMask, exMask, -30) +
          dp(pos + 1, shiftedInMask + 1, shiftedExMask, inCount - 1, exCount)
          if inCount > 0
          else -math.inf)
      placeExtrovert = (
          40 + getPlacementCost(i, j, inMask, exMask, 20) +
          dp(pos + 1, shiftedInMask, shiftedExMask + 1, inCount, exCount - 1)
          if exCount > 0
          else -math.inf)
      return max(skip, placeIntrovert, placeExtrovert)

    return dp(0, 0, 0, introvertsCount, extrovertsCount)