Talentd
FleetCode
Make and follow sheets.Sign in

Maximize Grid Happiness solution and explanation

HardBit ManipulationBitmaskDynamic ProgrammingMemoization13 min read41.6% acceptanceUpdated 9 Apr 2026
花花酱 LeetCode 1659. Maximize Grid Happiness - 刷题找工作 EP371Hua Hua · 21:37 · 2K viewsWatch on YouTube
5 more videos on this problem
  1. 花花酱 LeetCode 1659. Maximize Grid Happiness - 刷题找工作 EP371Hua Hua · 21:37 · 2K views
  2. LeetCode 1659. Maximize Grid HappinessHappy Coding · 36:04 · 1K views
  3. 1659. Maximize Grid Happiness (Leetcode Hard)Programming Live with Larry · 1:28:00 · 639 views
  4. 【每日一题】1659. Maximize Grid Happiness, 11/18/2020Huifeng Guan · 49:45 · 517 views
  5. Leetcode 1659. Maximize Grid HappinessFearless Learner · 49:42 · 210 views
  6. LeetCode 1659: Maximize Grid HappinessAlitaCode · 36:25 · 12 views

The problem

You are given four integers, m, n, introvertsCount, and extrovertsCount. You have an m x n grid, and there are two types of people: introverts and extroverts. There are introvertsCount introverts and extrovertsCount extroverts.

You should decide how many people you want to live in the grid and assign each of them one grid cell. Note that you do not have to have all the people living in the grid.

The happiness of each person is calculated as follows:

  • Introverts start with 120 happiness and lose 30 happiness for each neighbor (introvert or extrovert).
  • Extroverts start with 40 happiness and gain 20 happiness for each neighbor (introvert or extrovert).

Neighbors live in the directly adjacent cells north, east, south, and west of a person's cell.

The grid happiness is the sum of each person's happiness. Return the maximum possible grid happiness.

Example 1:

Input: m = 2, n = 3, introvertsCount = 1, extrovertsCount = 2
Output: 240
Explanation: Assume the grid is 1-indexed with coordinates (row, column).
We can put the introvert in cell (1,1) and put the extroverts in cells (1,3) and (2,3).
- Introvert at (1,1) happiness: 120 (starting happiness) - (0 * 30) (0 neighbors) = 120
- Extrovert at (1,3) happiness: 40 (starting happiness) + (1 * 20) (1 neighbor) = 60
- Extrovert at (2,3) happiness: 40 (starting happiness) + (1 * 20) (1 neighbor) = 60
The grid happiness is 120 + 60 + 60 = 240.
The above figure shows the grid in this example with each person's happiness. The introvert stays in the light green cell while the extroverts live on the light purple cells.

Example 2:

Input: m = 3, n = 1, introvertsCount = 2, extrovertsCount = 1
Output: 260
Explanation: Place the two introverts in (1,1) and (3,1) and the extrovert at (2,1).
- Introvert at (1,1) happiness: 120 (starting happiness) - (1 * 30) (1 neighbor) = 90
- Extrovert at (2,1) happiness: 40 (starting happiness) + (2 * 20) (2 neighbors) = 80
- Introvert at (3,1) happiness: 120 (starting happiness) - (1 * 30) (1 neighbor) = 90
The grid happiness is 90 + 80 + 90 = 260.

Example 3:

Input: m = 2, n = 2, introvertsCount = 4, extrovertsCount = 0
Output: 240

Constraints:

  • 1 <= m, n <= 5
  • 0 <= introvertsCount, extrovertsCount <= min(m * n, 6)

Approach overview

Problem Overview: You are given an m x n grid and a limited number of introverts and extroverts. Each person contributes a base happiness value, but neighboring cells change that value depending on the combination of people placed. The goal is to place people in the grid so the total happiness score is maximized.

Approach 1: Backtracking with Pruning (Exponential Time, O(3^(m*n)) time, O(m*n) space)

This approach explores every possible way to fill the grid: leave the cell empty, place an introvert, or place an extrovert. During recursion you track remaining introverts/extroverts and compute the incremental happiness contribution from the top and left neighbors. Pruning helps reduce work by stopping branches where remaining cells cannot beat the current best score. While conceptually straightforward, the branching factor of three per cell leads to exponential complexity, so it only works because the grid size is small. This method is useful for understanding the scoring mechanics before introducing dynamic programming.

Approach 2: Dynamic Programming with State Representation (O(m * 3^n * 3^n * introverts * extroverts) time, O(3^n * introverts * extroverts) space)

The optimal solution compresses each row configuration into a base‑3 state where each cell is 0 = empty, 1 = introvert, or 2 = extrovert. Since n ≤ 5, there are at most 3^5 = 243 possible row states. Precompute three things for every state: how many introverts/extroverts it uses, the internal row happiness, and the interaction score with another row above it. Then run DP row by row where the state is dp[row][prevState][introvertsLeft][extrovertsLeft]. For each row you iterate over all valid current states and combine them with the previous row state. This captures both horizontal and vertical neighbor effects while dramatically reducing the search space. The state compression technique is a classic combination of bitmask/state encoding and memoization to reuse overlapping subproblems.

Recommended for interviews: The dynamic programming state‑compression approach is what interviewers expect. Backtracking shows you understand the constraints and happiness rules, but it scales poorly. The DP solution demonstrates stronger problem‑solving skills: recognizing small grid width, encoding row states efficiently, and reusing results with memoization.

Approach 1: Dynamic Programming with State Representation

The problem can be tackled using Dynamic Programming (DP) by representing each cell's state in the grid. You evaluate placing introverts, extroverts, or leaving a cell empty, tracking the happiness and transitions.

Define a DP array dp[cells][in][ex] where cells is the linearized grid index, in is the count of introverts placed, and ex is the count of extroverts placed. Compute maximum happiness for placing persons in a cell conditioned on its previous state.

def getMaxGridHappiness(m, n, introvertsCount, extrovertsCount):
    # We initialize a DP dictionary where keys are tuples of (row, mask, in_cnt, ex_cnt)
    # and values are the maximum possible happiness for that state.
    from functools import lru_cache
    
    @lru_cache(None)
    def dp(pos, mask, in_cnt, ex_cnt):
        if pos == m * n or (in_cnt == 0 and ex_cnt == 0):
            return 0

        x, y = divmod(pos, n)

        best = dp(pos + 1, mask >> 2, in_cnt, ex_cnt)  # Try leaving this cell empty

        def score(new, old):
            return (120 if new == 1 else 40) + (-30 if new == 1 else 20) * (old == 1) + (20 if new == 2 else -30) * (old == 2)

        for k, new_cnt in [(1, in_cnt - 1), (2, ex_cnt - 1)]:
            if new_cnt >= 0:
                new_mask = ((mask >> 2) | (k << ((n - 1) * 2)))
                s = 0
                if y > 0:
                    s += score(k, (mask & 3))
                if x > 0:
                    s += score(k, (mask >> ((n - 1) * 2)) & 3)
                best = max(best, dp(pos + 1, new_mask, new_cnt if k == 1 else in_cnt, new_cnt if k == 2 else ex_cnt) + s)

        return best

    return dp(0, 0, introvertsCount, extrovertsCount)
public class GridHappiness {
    private static final int I_PLUS = 120;
    private static final int I_MINUS = 30;
    private static final int E_BASE = 40;
    private static final int E_PLUS = 20;

    private int[][][][] memo;

    public int getMaxGridHappiness(int m, int n, int introvertsCount, int extrovertsCount) {
        memo = new int[m * n][introvertsCount + 1][extrovertsCount + 1][1 << (2 * n)];
        for (int i = 0; i < m * n; i++)
            for (int j = 0; j <= introvertsCount; j++)
                for (int k = 0; k <= extrovertsCount; k++)
                    Arrays.fill(memo[i][j][k], -1);

        return dfs(0, introvertsCount, extrovertsCount, 0, m, n);
    }

    private int dfs(int pos, int in, int ex, int mask, int m, int n) {
        if (pos == m * n || (in == 0 && ex == 0))
            return 0;
        if (memo[pos][in][ex][mask] != -1)
            return memo[pos][in][ex][mask];

        int x = pos / n;
        int y = pos % n;

        // Shift current line left
        int left = mask % 4;
        int above = (mask >> (2 * (n - 1))) % 4;
        // Consider leaving cell empty
        int best = dfs(pos + 1, in, ex, mask >> 2, m, n);

        if (in > 0) {
            int happiness = I_PLUS;
            if (y > 0 && left != 0)
                happiness -= I_MINUS;
            if (x > 0 && above != 0)
                happiness -= I_MINUS;

            best = Math.max(best, dfs(pos + 1, in - 1, ex, (mask >> 2) + (1 << 2 * (n - 1)), m, n) + happiness);
        }

        if (ex > 0) {
            int happiness = E_BASE;
            if (y > 0 && left != 0)
                happiness += E_PLUS;
            if (x > 0 && above != 0)
                happiness += E_PLUS;

            best = Math.max(best, dfs(pos + 1, in, ex - 1, (mask >> 2) + (2 << 2 * (n - 1)), m, n) + happiness);
        }

        return memo[pos][in][ex][mask] = best;
    }
}
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;

int getMaxGridHappiness(int m, int n, int introvertsCount, int extrovertsCount) {
    // Define happiness values
    const int I_PLUS = 120, I_MINUS = 30, E_PLUS = 20, E_BASE = 40;

    int memo[25][7][7][1 << 6];  // Maximum for m,n <= 5 gives 25 states max for counts, masks '1 << 6'
    memset(memo, -1, sizeof(memo));

    function<int(int, int, int, int)> dfs = [&](int pos, int in, int ex, int mask) {
        if (pos == m * n || (in == 0 && ex == 0)) return 0;
        if (memo[pos][in][ex][mask] != -1) return memo[pos][in][ex][mask];

        int x = pos / n;
        int y = pos % n;
        int state = (mask >> ((n - 1) * 2)) & 3;  // Extract the first cell state from the mask
        int left = ((mask >> (2 * (n - 1))) & 3);  // Previous state - row
        int above = (mask & 3);  // State of the top neighbor

        int best = dfs(pos + 1, in, ex, mask >> 2);  // Option to leave cell empty

        // Place an introvert
        if (in > 0) {
            int happiness = I_PLUS;
            if (y) happiness -= I_MINUS * above;
            if (x) happiness -= I_MINUS * left;

            best = max(best, dfs(pos + 1, in - 1, ex, (mask >> 2) | (1 << (n-1) * 2)) + happiness);
        }

        // Place an extrovert
        if (ex > 0) {
            int happiness = E_BASE;
            if (y) happiness += E_PLUS * above;
            if (x) happiness += E_PLUS * left;

            best = max(best, dfs(pos + 1, in, ex - 1, (mask >> 2) | (2 << (n-1) * 2)) + happiness);
        }

        return memo[pos][in][ex][mask] = best;
    };

    return dfs(0, introvertsCount, extrovertsCount, 0);
}

The solution uses a recursive function with memoization to track happiness states in a grid traversed in a linearized fashion. Each cell considers the option of being empty, occupied by an introvert, or occupied by an extrovert. The recursive state space branches according to available introverts and extroverts, computing transitions based on current grid neighbors using bitmask representation for fast neighbors computation.

Complexity

Time complexity: O(m * n * 3^(m*n) * introvertsCount * extrovertsCount). Space complexity: O(m * n * 3^(m*n) * introvertsCount * extrovertsCount) due to memoization storage being used to store intermediate states.

Try this approach in the editor

Approach 2: Backtracking with Pruning

The problem can alternatively be approached using backtracking with pruning. This technique evaluates possible placements of introverts and extroverts in the grid, backtracking when a placement results in a sub-optimal state.

def getMaxGridHappiness(m, n, introvertsCount, extrovertsCount):
    def dfs(grid, i, in_remaining, ex_remaining):
        if i == m * n:
            return calculate_happiness(grid, m, n)
        r, c = divmod(i, n)
        max_happiness = dfs(grid, i + 1, in_remaining, ex_remaining)  # Skip this cell
        if in_remaining > 0:
            grid[r][c] = 'I'
            max_happiness = max(max_happiness, dfs(grid, i + 1, in_remaining - 1, ex_remaining))
            grid[r][c] = '.'
        if ex_remaining > 0:
            grid[r][c] = 'E'
            max_happiness = max(max_happiness, dfs(grid, i + 1, in_remaining, ex_remaining - 1))
            grid[r][c] = '.'
        return max_happiness

    def calculate_happiness(grid, m, n):
        happiness = 0
        directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
        for r in range(m): 
            for c in range(n):
                if grid[r][c] != '.':
                    if grid[r][c] == 'I':
                        happiness += 120
                    else:
                        happiness += 40
                    for dr, dc in directions:
                        nr, nc = r + dr, c + dc
                        if 0 <= nr < m and 0 <= nc < n and grid[nr][nc] != '.':
                            if grid[r][c] == 'I':
                                happiness -= 30
                            else:
                                happiness += 20
        return happiness

    grid = [['.'] * n for _ in range(m)]
    return dfs(grid, 0, introvertsCount, extrovertsCount)
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;

class Solution {
    int result;
    vector<vector<char>> grid;
    int m, n;
    vector<pair<int, int>> directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

    int calculate_happiness() {
        int happiness = 0;
        for (int r = 0; r < m; ++r) {
            for (int c = 0; c < n; ++c) {
                if (grid[r][c] != '.') {
                    if (grid[r][c] == 'I') happiness += 120;
                    else happiness += 40;
                    for (const auto& d : directions) {
                        int nr = r + d.first, nc = c + d.second;
                        if (nr >= 0 && nc >= 0 && nr < m && nc < n && grid[nr][nc] != '.') {
                            if (grid[r][c] == 'I') happiness -= 30;
                            else happiness += 20;
                        }
                    }
                }
            }
        }
        return happiness;
    }

    void dfs(int i, int in_remaining, int ex_remaining) {
        if (i == m * n) {
            result = max(result, calculate_happiness());
            return;
        }
        int r = i / n, c = i % n;
        dfs(i + 1, in_remaining, ex_remaining); // skip cell
        if (in_remaining > 0) {
            grid[r][c] = 'I';
            dfs(i + 1, in_remaining - 1, ex_remaining);
            grid[r][c] = '.';
        }
        if (ex_remaining > 0) {
            grid[r][c] = 'E';
            dfs(i + 1, in_remaining, ex_remaining - 1);
            grid[r][c] = '.';
        }
    }

public:
    int getMaxGridHappiness(int m, int n, int introvertsCount, int extrovertsCount) {
        this->m = m;
        this->n = n;
        grid = vector<vector<char>>(m, vector<char>(n, '.'));
        result = 0;
        dfs(0, introvertsCount, extrovertsCount);
        return result;
    }
};

This solution uses a backtracking approach to explore all possible ways to place people in the grid. The function recursively checks each cell and tries placing an introvert, extrovert, or leaving it empty. It prunes non-promising branches based on remaining counts of introverts and extroverts.

Complexity

Time Complexity: O(3^(m*n)) due to the exhaustive traversal through the potential grid arrangements. Space Complexity: O(m * n) for the grid representation.

Try this approach in the editor

Approach 3: Ternary State Compression + Memoization

We notice that in the problem, 1 leq m, n leq 5, and each grid cell has three states: no personnel assigned, introverted personnel assigned, and extroverted personnel assigned. Therefore, we can use 0, 1, 2 to represent these three states, and each row in the grid can be represented by a ternary number of length n.

We define a function dfs(i, pre, ic, ec), which represents the maximum happiness of the grid starting from the i-th row, with the state of the previous row being pre, ic introverted people left, and ec extroverted people left. The answer is dfs(0, 0, introvertsCount, extrovertsCount).

The calculation process of the function dfs(i, pre, ic, ec) is as follows:

If i = m, it means that all rows have been processed, so return 0;

If ic = 0 and ec = 0, it means that all people have been assigned, so return 0;

Otherwise, enumerate the current row's state cur, where cur \in [0, 3^n), then calculate the happiness f[cur] of the current row, and the contribution g[pre][cur] to the happiness between the state pre of the previous row, and recursively calculate dfs(i + 1, cur, ic - ix[cur], ec - ex[cur]), finally return the maximum value of f[cur] + g[pre][cur] + dfs(i + 1, cur, ic - ix[cur], ec - ex[cur]), that is:

$ dfs(i, pre, ic, ec) = max_{cur} {f[cur] + g[pre][cur] + dfs(i + 1, cur, ic - ix[cur], ec - ex[cur])}

Where:

  • ix[cur] represents the number of introverted people in state cur;
  • ex[cur] represents the number of extroverted people in state cur;
  • f[cur] represents the initial happiness of the people in state cur;
  • g[pre][cur] represents the contribution to happiness of two adjacent state rows.

These values can be obtained through preprocessing. And we can use the method of memoization to avoid repeated calculations.

The time complexity is O(3^{2n} times (m times ic times ec + n)), and the space complexity is O(3^{2n} + 3^n times m times ic times ec). Where ic and ec$ represent the number of introverted and extroverted people, respectively.

function getMaxGridHappiness(
    m: number,
    n: number,
    introvertsCount: number,
    extrovertsCount: number,
): number {
    const mx = 3 ** n;
    const f: number[] = Array(mx).fill(0);
    const g: number[][] = Array(mx)
        .fill(0)
        .map(() => Array(mx).fill(0));
    const h: number[][] = [
        [0, 0, 0],
        [0, -60, -10],
        [0, -10, 40],
    ];
    const bits: number[][] = Array(mx)
        .fill(0)
        .map(() => Array(n).fill(0));
    const ix: number[] = Array(mx).fill(0);
    const ex: number[] = Array(mx).fill(0);
    const memo: number[][][][] = Array(m)
        .fill(0)
        .map(() =>
            Array(mx)
                .fill(0)
                .map(() =>
                    Array(introvertsCount + 1)
                        .fill(0)
                        .map(() => Array(extrovertsCount + 1).fill(-1)),
                ),
        );
    for (let i = 0; i < mx; ++i) {
        let mask = i;
        for (let j = 0; j < n; ++j) {
            const x = mask % 3;
            mask = Math.floor(mask / 3);
            bits[i][j] = x;
            if (x === 1) {
                ix[i] += 1;
                f[i] += 120;
            } else if (x === 2) {
                ex[i] += 1;
                f[i] += 40;
            }
            if (j > 0) {
                f[i] += h[x][bits[i][j - 1]];
            }
        }
    }
    for (let i = 0; i < mx; ++i) {
        for (let j = 0; j < mx; ++j) {
            for (let k = 0; k < n; ++k) {
                g[i][j] += h[bits[i][k]][bits[j][k]];
            }
        }
    }
    const dfs = (i: number, pre: number, ic: number, ec: number): number => {
        if (i === m || (ic === 0 && ec === 0)) {
            return 0;
        }
        if (memo[i][pre][ic][ec] !== -1) {
            return memo[i][pre][ic][ec];
        }
        let ans = 0;
        for (let cur = 0; cur < mx; ++cur) {
            if (ix[cur] <= ic && ex[cur] <= ec) {
                const a = f[cur] + g[pre][cur];
                const b = dfs(i + 1, cur, ic - ix[cur], ec - ex[cur]);
                ans = Math.max(ans, a + b);
            }
        }
        return (memo[i][pre][ic][ec] = ans);
    };
    return dfs(0, 0, introvertsCount, extrovertsCount);
}
func getMaxGridHappiness(m int, n int, introvertsCount int, extrovertsCount int) int {
	mx := int(math.Pow(3, float64(n)))
	f := make([]int, mx)
	g := make([][]int, mx)
	h := [3][3]int{{0, 0, 0}, {0, -60, -10}, {0, -10, 40}}
	bits := make([][]int, mx)
	ix := make([]int, mx)
	ex := make([]int, mx)
	memo := make([][][][]int, m)
	for i := range g {
		g[i] = make([]int, mx)
		bits[i] = make([]int, n)
	}
	for i := range memo {
		memo[i] = make([][][]int, mx)
		for j := range memo[i] {
			memo[i][j] = make([][]int, introvertsCount+1)
			for k := range memo[i][j] {
				memo[i][j][k] = make([]int, extrovertsCount+1)
				for l := range memo[i][j][k] {
					memo[i][j][k][l] = -1
				}
			}
		}
	}
	for i := 0; i < mx; i++ {
		mask := i
		for j := 0; j < n; j++ {
			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 i := 0; i < mx; i++ {
		for j := 0; j < mx; j++ {
			for k := 0; k < n; k++ {
				g[i][j] += h[bits[i][k]][bits[j][k]]
			}
		}
	}
	var dfs func(int, int, int, int) int
	dfs = func(i, pre, ic, ec int) int {
		if i == m || (ic == 0 && ec == 0) {
			return 0
		}
		if memo[i][pre][ic][ec] != -1 {
			return memo[i][pre][ic][ec]
		}
		ans := 0
		for cur := 0; cur < mx; cur++ {
			if ix[cur] <= ic && ex[cur] <= ec {
				ans = max(ans, f[cur]+g[pre][cur]+dfs(i+1, cur, ic-ix[cur], ec-ex[cur]))
			}
		}
		memo[i][pre][ic][ec] = ans
		return ans
	}
	return dfs(0, 0, introvertsCount, extrovertsCount)
}
Try this approach in the editor

Approach 4: Contour Line Memorized Search

We can consider searching each grid cell, each time searching a position (i, j), we denote pos = i times n + j. Then its left and upper adjacent grids will affect their happiness contribution.

We define a function dfs(pos, pre, ic, ec), which represents the maximum happiness of the grid when we search to position pos, and the state of the previous n grid cells is pre, with ic introverted people left, and ec extroverted people left. The answer is dfs(0, 0, introvertsCount, extrovertsCount).

The calculation process of the function dfs(pos, pre, ic, ec) is as follows:

If pos = m times n, it means that all grid cells have been processed, so return 0;

If ic = 0 and ec = 0, it means that all people have been assigned, so return 0;

Otherwise, we calculate the state up = \frac{pre}{3^{n-1}} of the upper adjacent grid cell of the current grid cell based on pre, and the state left = pre bmod 3 of the left adjacent grid cell (note that if pos is in the 0-th column, then left = 0).

Next, we enumerate the state i of the current grid cell, where i \in [0, 3). Then the state of the current n grid cells is cur = pre bmod 3^{n-1} times 3 + i, and the happiness contribution of the current grid cell and its left and upper adjacent grid cells is h[up][i]+h[left][i]; the happiness of the current grid cell itself depends on whether personnel are assigned to this position, and whether the assigned personnel are introverted or extroverted. If they are introverted, the happiness is 120, if they are extroverted, the happiness is 40, otherwise, the happiness is 0; then, if personnel are assigned to the current grid cell, we need to update ic or ec when we recursively call the function. Accumulate these happiness values, and take the maximum value as the return value of the function.

Similar to Solution 1, we can also use the method of memoization to avoid repeated calculations.

The time complexity is O(3^{n+1} times m times n times ic times ec), and the space complexity is O(3^n times m times n times ic times ec). Where ic and ec represent the number of introverted and extroverted people, respectively.

function getMaxGridHappiness(
    m: number,
    n: number,
    introvertsCount: number,
    extrovertsCount: number,
): number {
    const p = 3 ** (n - 1);
    const h: number[][] = [
        [0, 0, 0],
        [0, -60, -10],
        [0, -10, 40],
    ];
    const memo: number[][][][] = Array(m * n)
        .fill(0)
        .map(() =>
            Array(p * 3)
                .fill(0)
                .map(() =>
                    Array(introvertsCount + 1)
                        .fill(0)
                        .map(() => Array(extrovertsCount + 1).fill(-1)),
                ),
        );
    const dfs = (pos: number, pre: number, ic: number, ec: number): number => {
        if (pos === m * n || (ic === 0 && ec === 0)) {
            return 0;
        }
        if (memo[pos][pre][ic][ec] !== -1) {
            return memo[pos][pre][ic][ec];
        }
        let ans = 0;
        const up = Math.floor(pre / p);
        const left = pos % n == 0 ? 0 : pre % 3;
        for (let i = 0; i < 3; ++i) {
            if ((i === 1 && ic === 0) || (i === 2 && ec === 0)) {
                continue;
            }
            const cur = (pre % p) * 3 + i;
            const a = h[up][i] + h[left][i];
            const nic = i === 1 ? ic - 1 : ic;
            const nec = i === 2 ? ec - 1 : ec;
            const b = dfs(pos + 1, cur, nic, nec);
            const c = i === 1 ? 120 : i === 2 ? 40 : 0;
            ans = Math.max(ans, a + b + c);
        }
        return (memo[pos][pre][ic][ec] = ans);
    };
    return dfs(0, 0, introvertsCount, extrovertsCount);
}
func getMaxGridHappiness(m int, n int, introvertsCount int, extrovertsCount int) int {
	p := int(math.Pow(3, float64(n-1)))
	h := [3][3]int{{0, 0, 0}, {0, -60, -10}, {0, -10, 40}}
	memo := make([][][][]int, m*n)
	for i := range memo {
		memo[i] = make([][][]int, p*3)
		for j := range memo[i] {
			memo[i][j] = make([][]int, introvertsCount+1)
			for k := range memo[i][j] {
				memo[i][j][k] = make([]int, extrovertsCount+1)
				for l := range memo[i][j][k] {
					memo[i][j][k][l] = -1
				}
			}
		}
	}
	var dfs func(int, int, int, int) int
	dfs = func(pos, pre, ic, ec int) int {
		if pos == m*n || (ic == 0 && ec == 0) {
			return 0
		}
		if memo[pos][pre][ic][ec] != -1 {
			return memo[pos][pre][ic][ec]
		}
		ans := 0
		up := pre / p
		left := pre % 3
		if pos%n == 0 {
			left = 0
		}
		for i := 0; i < 3; i++ {
			if (i == 1 && ic == 0) || (i == 2 && ec == 0) {
				continue
			}
			cur := pre%p*3 + i
			nic, nec := ic, ec
			c := 0
			if i == 1 {
				nic--
				c = 120
			} else if i == 2 {
				nec--
				c = 40
			}
			a := h[up][i] + h[left][i]
			b := dfs(pos+1, cur, nic, nec)
			ans = max(ans, a+b+c)
		}
		memo[pos][pre][ic][ec] = ans
		return ans
	}
	return dfs(0, 0, introvertsCount, extrovertsCount)
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Dynamic Programming with State Representation

Time complexity: O(m * n * 3^(m*n) * introvertsCount * extrovertsCount). Space complexity: O(m * n * 3^(m*n) * introvertsCount * extrovertsCount) due to memoization storage being used to store intermediate states.

Backtracking with Pruning

Time Complexity: O(3^(m*n)) due to the exhaustive traversal through the potential grid arrangements. Space Complexity: O(m * n) for the grid representation.

Ternary State Compression + Memoization–
Contour Line Memorized Search–

Complexity analysis

ApproachTimeSpaceWhen to Use
Backtracking with PruningO(3^(m*n))O(m*n)Good for understanding the scoring rules and brute-force reasoning on very small grids.
Dynamic Programming with Row State CompressionO(m * 3^n * 3^n * I * E)O(3^n * I * E)Optimal approach when grid width is small (n ≤ 5). Efficiently handles neighbor interactions using precomputed row states.

Frequently asked questions

Is Maximize Grid Happiness easy or hard?

Maximize Grid Happiness is classified as a Hard problem. The difficulty comes from modeling neighbor interactions and recognizing that the grid width constraint allows state compression with dynamic programming.

Maximize Grid Happiness Python/Java solution

Python, Java, and C++ implementations typically follow the same structure: generate all row states, precompute happiness and row interactions, then run DP across rows with memoization. Python often uses recursion with caching, while Java/C++ use arrays or hash maps for DP states.

How to solve Maximize Grid Happiness in O(n)?

The problem cannot be solved in strict O(n) time because interactions between neighbors must be evaluated across grid configurations. The most efficient known approach uses dynamic programming with state compression, reducing the search space from exponential in cells to manageable row states.

What is the best approach for Maximize Grid Happiness?

The best approach uses dynamic programming with row state compression. Each row is encoded as a base-3 state representing empty, introvert, or extrovert cells. Precomputing row happiness and transitions between adjacent rows allows the DP to evaluate placements efficiently while tracking remaining introverts and extroverts.

Is Maximize Grid Happiness asked at Google/Amazon/Meta?

Grid DP problems with state compression are common in interviews at companies like Google, Amazon, and Meta. This specific problem is a classic example used to evaluate dynamic programming skills, bitmask/state encoding, and handling local interaction effects.

What data structure is used in Maximize Grid Happiness?

The solution primarily uses dynamic programming tables along with encoded row states. Bitmask or base‑3 state representations store each row configuration, while memoization caches results for combinations of row index, previous state, and remaining people.

What is the time complexity of Maximize Grid Happiness?

The optimized dynamic programming solution runs in roughly O(m * 3^n * 3^n * I * E), where n is the grid width and I/E are the remaining introverts and extroverts. Because n ≤ 5, the number of row states is at most 243, which keeps the solution practical.

Comments

Now write it yourselfClose this page and solve Maximize Grid Happiness in the editor, without looking.
Practise Maximize Grid Happiness