1659. 最大化网格幸福感
给你四个整数 m
、n
、introvertsCount
和 extrovertsCount
。有一个 m x n
网格,和两种类型的人:内向的人和外向的人。总共有 introvertsCount
个内向的人和 extrovertsCount
个外向的人。
请你决定网格中应当居住多少人,并为每个人分配一个网格单元。 注意,不必 让所有人都生活在网格中。
每个人的 幸福感 计算如下:
120
个幸福感,但每存在一个邻居(内向的或外向的)他都会 失去 30
个幸福感。40
个幸福感,每存在一个邻居(内向的或外向的)他都会 得到 20
个幸福感。邻居是指居住在一个人所在单元的上、下、左、右四个直接相邻的单元中的其他人。
网格幸福感 是每个人幸福感的 总和 。 返回 最大可能的网格幸福感 。
示例 1:
输入:m = 2, n = 3, introvertsCount = 1, extrovertsCount = 2 输出:240 解释:假设网格坐标 (row, column) 从 1 开始编号。 将内向的人放置在单元 (1,1) ,将外向的人放置在单元 (1,3) 和 (2,3) 。 - 位于 (1,1) 的内向的人的幸福感:120(初始幸福感)- (0 * 30)(0 位邻居)= 120 - 位于 (1,3) 的外向的人的幸福感:40(初始幸福感)+ (1 * 20)(1 位邻居)= 60 - 位于 (2,3) 的外向的人的幸福感:40(初始幸福感)+ (1 * 20)(1 位邻居)= 60 网格幸福感为:120 + 60 + 60 = 240 上图展示该示例对应网格中每个人的幸福感。内向的人在浅绿色单元中,而外向的人在浅紫色单元中。
示例 2:
输入:m = 3, n = 1, introvertsCount = 2, extrovertsCount = 1 输出:260 解释:将内向的人放置在单元 (1,1) 和 (3,1) ,将外向的人放置在单元 (2,1) 。 - 位于 (1,1) 的内向的人的幸福感:120(初始幸福感)- (1 * 30)(1 位邻居)= 90 - 位于 (2,1) 的外向的人的幸福感:40(初始幸福感)+ (2 * 20)(2 位邻居)= 80 - 位于 (3,1) 的内向的人的幸福感:120(初始幸福感)- (1 * 30)(1 位邻居)= 90 网格幸福感为 90 + 80 + 90 = 260
示例 3:
输入:m = 2, n = 2, introvertsCount = 4, extrovertsCount = 0 输出:240
提示:
1 <= m, n <= 5
0 <= introvertsCount, extrovertsCount <= min(m * n, 6)
原站题解
golang 解法, 执行用时: 48 ms, 内存消耗: 7.2 MB, 提交时间: 2023-06-24 11:38:08
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) } func max(a, b int) int { if a > b { return a } return b }
java 解法, 执行用时: 49 ms, 内存消耗: 52.4 MB, 提交时间: 2023-06-24 11:37:47
class Solution { private int m; private int n; private int p; private final int[][] h = {{0, 0, 0}, {0, -60, -10}, {0, -10, 40}}; private Integer[][][][] memo; public int getMaxGridHappiness(int m, int n, int introvertsCount, int extrovertsCount) { this.m = m; this.n = n; p = (int) Math.pow(3, n - 1); memo = new Integer[m * n][p * 3][introvertsCount + 1][extrovertsCount + 1]; return dfs(0, 0, introvertsCount, extrovertsCount); } private int dfs(int pos, int pre, int ic, int ec) { if (pos == m * n || (ic == 0 && ec == 0)) { return 0; } if (memo[pos][pre][ic][ec] != null) { return memo[pos][pre][ic][ec]; } int ans = 0; int up = pre / p; int left = pos % n == 0 ? 0 : pre % 3; for (int i = 0; i < 3; ++i) { if (i == 1 && (ic == 0) || (i == 2 && ec == 0)) { continue; } int cur = pre % p * 3 + i; int a = h[up][i] + h[left][i]; int b = dfs(pos + 1, cur, ic - (i == 1 ? 1 : 0), ec - (i == 2 ? 1 : 0)); int c = i == 1 ? 120 : (i == 2 ? 40 : 0); ans = Math.max(ans, a + b + c); } return memo[pos][pre][ic][ec] = ans; } }
python3 解法, 执行用时: 1364 ms, 内存消耗: 141.6 MB, 提交时间: 2023-06-24 11:37:21
class Solution: def getMaxGridHappiness( self, m: int, n: int, introvertsCount: int, extrovertsCount: int ) -> int: @cache def dfs(pos: int, pre: int, ic: int, ec: int) -> int: if pos == m * n or (ic == 0 and ec == 0): return 0 ans = 0 up = pre // p left = 0 if pos % n == 0 else pre % 3 for i in range(3): if (i == 1 and ic == 0) or (i == 2 and ec == 0): continue cur = pre % p * 3 + i a = h[up][i] + h[left][i] b = dfs(pos + 1, cur, ic - (i == 1), ec - (i == 2)) c = 0 if i == 1: c = 120 elif i == 2: c = 40 ans = max(ans, a + b + c) return ans p = pow(3, n - 1) h = [[0, 0, 0], [0, -60, -10], [0, -10, 40]] return dfs(0, 0, introvertsCount, extrovertsCount)
golang 解法, 执行用时: 124 ms, 内存消耗: 6.8 MB, 提交时间: 2023-06-24 11:36:00
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) } func max(a, b int) int { if a > b { return a } return b }
python3 解法, 执行用时: 1256 ms, 内存消耗: 68.1 MB, 提交时间: 2023-06-24 11:35:36
class Solution: def getMaxGridHappiness(self, m: int, n: int, nx: int, wx: int) -> int: # 如果 x 和 y 相邻,需要加上的分数 def calc(x: int, y: int) -> int: if x == 0 or y == 0: return 0 # 例如两个内向的人,每个人要 -30,一共 -60,下同 if x == 1 and y == 1: return -60 if x == 2 and y == 2: return 40 return -10 n3 = 3**n # 预处理:每一个 mask 的三进制表示 mask_span = dict() # 预处理:每一个 mask 去除最高位、乘 3、加上新的最低位的结果 truncate = dict() # 预处理 highest = n3 // 3 for mask in range(n3): mask_tmp = mask bits = list() for i in range(n): bits.append(mask_tmp % 3) mask_tmp //= 3 # 与方法一不同的是,这里需要反过来存储,这样 [0] 对应最高位,[n-1] 对应最低位 mask_span[mask] = bits[::-1] truncate[mask] = [ mask % highest * 3, mask % highest * 3 + 1, mask % highest * 3 + 2, ] # dfs(位置,轮廓线上的 mask,剩余的内向人数,剩余的外向人数) @lru_cache(None) def dfs(pos: int, borderline: int, nx: int, wx: int): # 边界条件:如果已经处理完,或者没有人了 if pos == m * n or nx + wx == 0: return 0 x, y = divmod(pos, n) # 什么都不做 best = dfs(pos + 1, truncate[borderline][0], nx, wx) # 放一个内向的人 if nx > 0: best = max(best, 120 + calc(1, mask_span[borderline][0]) \ + (0 if y == 0 else calc(1, mask_span[borderline][n - 1])) \ + dfs(pos + 1, truncate[borderline][1], nx - 1, wx)) # 放一个外向的人 if wx > 0: best = max(best, 40 + calc(2, mask_span[borderline][0]) \ + (0 if y == 0 else calc(2, mask_span[borderline][n - 1])) \ + dfs(pos + 1, truncate[borderline][2], nx, wx - 1)) return best return dfs(0, 0, nx, wx)
java 解法, 执行用时: 200 ms, 内存消耗: 43 MB, 提交时间: 2023-06-24 11:34:01
class Solution { static final int T = 243, N = 5, M = 6; int n, m, tot; int[][] maskBits; int[] ivCount; int[] evCount; int[] innerScore; int[][] interScore; int[][][][] d; // 邻居间的分数 static int[][] score = { {0, 0, 0}, {0, -60, -10}, {0, -10, 40} }; public int getMaxGridHappiness(int m, int n, int introvertsCount, int extrovertsCount) { this.n = n; this.m = m; // 状态总数为 3^n this.tot = (int) Math.pow(3, n); this.maskBits = new int[T][N]; this.ivCount = new int[T]; this.evCount = new int[T]; this.innerScore = new int[T]; this.interScore = new int[T][T]; this.d = new int[N][T][M + 1][M + 1]; initData(); // 记忆化搜索数组,初始化为 -1,表示未赋值 for (int i = 0; i < N; i++) { for (int j = 0; j < T; j++) { for (int k = 0; k <= M; k++) { Arrays.fill(d[i][j][k], -1); } } } return dfs(0, 0, introvertsCount, extrovertsCount); } public void initData() { // 计算行内分数 for (int mask = 0; mask < tot; mask++) { int maskTmp = mask; for (int i = 0; i < n; i++) { int x = maskTmp % 3; maskBits[mask][i] = x; maskTmp /= 3; if (x == 1) { ivCount[mask]++; innerScore[mask] += 120; } else if (x == 2) { evCount[mask]++; innerScore[mask] += 40; } if (i > 0) { innerScore[mask] += score[x][maskBits[mask][i - 1]]; } } } // 计算行间分数 for (int i = 0; i < tot; i++) { for (int j = 0; j < tot; j++) { interScore[i][j] = 0; for (int k = 0; k < n; k++) { interScore[i][j] += score[maskBits[i][k]][maskBits[j][k]]; } } } } public int dfs(int row, int premask, int iv, int ev) { if (row == m || (iv == 0 && ev == 0)) { return 0; } // 如果该状态已经计算过答案,则直接返回 if (d[row][premask][iv][ev] != -1) { return d[row][premask][iv][ev]; } // 合法状态,初始值为 0 int res = 0; for (int mask = 0; mask < tot; mask++) { // mask 包含的内向人数不能超过 iv ,外向人数不能超过 ev if (ivCount[mask] > iv || evCount[mask] > ev) { continue; } res = Math.max(res, dfs(row + 1, mask, iv - ivCount[mask], ev - evCount[mask]) + innerScore[mask] + interScore[premask][mask]); } d[row][premask][iv][ev] = res; return res; } }
javascript 解法, 执行用时: 552 ms, 内存消耗: 74.4 MB, 提交时间: 2023-06-24 11:33:35
/** * @param {number} m * @param {number} n * @param {number} introvertsCount * @param {number} extrovertsCount * @return {number} */ const T = 243; const N = 5; const M = 6; const score = [ [0, 0, 0], [0, -60, -10], [0, -10, 40] ]; function getMaxGridHappiness(m, n, introvertsCount, extrovertsCount) { let tot = Math.pow(3, n); let maskBits = new Array(T).fill(0).map(() => new Array(N).fill(0)); let ivCount = new Array(T).fill(0); let evCount = new Array(T).fill(0); let innerScore = new Array(T).fill(0); let interScore = new Array(T).fill(0).map(() => new Array(T).fill(0)); let d = new Array(N).fill(0).map(() => new Array(T).fill(0).map(() => new Array(M + 1).fill(0).map(() => new Array(M + 1).fill(-1)))); initData(); for (let i = 0; i < N; i++) { for (let j = 0; j < T; j++) { for (let k = 0; k <= M; k++) { d[i][j][k].fill(-1); } } } return dfs(0, 0, introvertsCount, extrovertsCount); function initData() { for (let mask = 0; mask < tot; mask++) { let maskTmp = mask; for (let i = 0; i < n; i++) { let x = maskTmp % 3; maskBits[mask][i] = x; maskTmp = Math.floor(maskTmp / 3); if (x === 1) { ivCount[mask]++; innerScore[mask] += 120; } else if (x === 2) { evCount[mask]++; innerScore[mask] += 40; } if (i > 0) { innerScore[mask] += score[x][maskBits[mask][i - 1]]; } } } for (let i = 0; i < tot; i++) { for (let j = 0; j < tot; j++) { interScore[i][j] = 0; for (let k = 0; k < n; k++) { interScore[i][j] += score[maskBits[i][k]][maskBits[j][k]]; } } } } function dfs(row, premask, iv, ev) { if (row === m || (iv === 0 && ev === 0)) { return 0; } if (d[row][premask][iv][ev] !== -1) { return d[row][premask][iv][ev]; } let res = 0; for (let mask = 0; mask < tot; mask++) { if (ivCount[mask] > iv || evCount[mask] > ev) { continue; } res = Math.max( res, dfs(row + 1, mask, iv - ivCount[mask], ev - evCount[mask]) + innerScore[mask] + interScore[premask][mask] ); } d[row][premask][iv][ev] = res; return res; } }
python3 解法, 执行用时: 5872 ms, 内存消耗: 38 MB, 提交时间: 2023-06-24 11:33:01
''' 状态压缩dp 每个网络三种状态:空置、内向、外向,记为0,1,2. 对每一行的状态使用长度为n的三进制数进行编码。 d(row, premask, iv, ev) 表示第row-1行的状态为premask, 并且row ~ m-1行最多放置iv个内向和ev个外向的人时的分数。 ''' class Solution: def getMaxGridHappiness(self, m: int, n: int, introvertsCount: int, extrovertsCount: int) -> int: T, N, M = 243, 5, 6 # 邻居间的分数 score = [ [0, 0, 0], [0, -60, -10], [0, -10, 40], ] tot = 3**n mask_bits = [[0] * N for _ in range(T)] iv_count, ev_count = [0] * T, [0] * T inner_score = [0] * T inter_score = [[0] * T for _ in range(T)] def init_data() -> None: # 计算行内分数 for mask in range(tot): mask_tmp = mask for i in range(n): x = mask_tmp % 3 mask_bits[mask][i] = x mask_tmp //= 3 if x == 1: iv_count[mask] += 1 inner_score[mask] += 120 elif x == 2: ev_count[mask] += 1 inner_score[mask] += 40 if i > 0: inner_score[mask] += score[x][mask_bits[mask][i - 1]] # 计算行间分数 for i in range(tot): for j in range(tot): for k in range(n): inter_score[i][j] += score[mask_bits[i][k]][mask_bits[j][k]] @cache def dfs(row: int, premask: int, iv: int, ev: int) -> int: if row == m or (iv == 0 and ev == 0): return 0 res = 0 for mask in range(tot): # mask 包含的内向人数不能超过 iv ,外向人数不能超过 ev if iv_count[mask] > iv or ev_count[mask] > ev: continue res = max(res, dfs(row + 1, mask, iv - iv_count[mask], ev - ev_count[mask]) + inner_score[mask] + inter_score[premask][mask]) return res init_data() return dfs(0, 0, introvertsCount, extrovertsCount)