Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
You are given a m x n 2D integer array grid and an integer k. You start at the top-left cell (0, 0) and your goal is to reach the bottom‐right cell (m - 1, n - 1).
There are two types of moves available:
Normal move: You can move right or down from your current cell (i, j), i.e. you can move to (i, j + 1) (right) or (i + 1, j) (down). The cost is the value of the destination cell.
Teleportation: You can teleport from any cell (i, j), to any cell (x, y) such that grid[x][y] <= grid[i][j]; the cost of this move is 0. You may teleport at most k times.
Return the minimum total cost to reach cell (m - 1, n - 1) from (0, 0).
Example 1:
Input: grid = [[1,3,3],[2,5,4],[4,3,5]], k = 2
Output: 7
Explanation:
Initially we are at (0, 0) and cost is 0.
| Current Position | Move | New Position | Total Cost |
|---|---|---|---|
(0, 0) |
Move Down | (1, 0) |
0 + 2 = 2 |
(1, 0) |
Move Right | (1, 1) |
2 + 5 = 7 |
(1, 1) |
Teleport to (2, 2) |
(2, 2) |
7 + 0 = 7 |
The minimum cost to reach bottom-right cell is 7.
Example 2:
Input: grid = [[1,2],[2,3],[3,4]], k = 1
Output: 9
Explanation:
Initially we are at (0, 0) and cost is 0.
| Current Position | Move | New Position | Total Cost |
|---|---|---|---|
(0, 0) |
Move Down | (1, 0) |
0 + 2 = 2 |
(1, 0) |
Move Right | (1, 1) |
2 + 3 = 5 |
(1, 1) |
Move Down | (2, 1) |
5 + 4 = 9 |
The minimum cost to reach bottom-right cell is 9.
Constraints:
2 <= m, n <= 80m == grid.lengthn == grid[i].length0 <= grid[i][j] <= 1040 <= k <= 10Problem summary: You are given a m x n 2D integer array grid and an integer k. You start at the top-left cell (0, 0) and your goal is to reach the bottom‐right cell (m - 1, n - 1). There are two types of moves available: Normal move: You can move right or down from your current cell (i, j), i.e. you can move to (i, j + 1) (right) or (i + 1, j) (down). The cost is the value of the destination cell. Teleportation: You can teleport from any cell (i, j), to any cell (x, y) such that grid[x][y] <= grid[i][j]; the cost of this move is 0. You may teleport at most k times. Return the minimum total cost to reach cell (m - 1, n - 1) from (0, 0).
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Dynamic Programming
[[1,3,3],[2,5,4],[4,3,5]] 2
[[1,2],[2,3],[3,4]] 1
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #3651: Minimum Cost Path with Teleportations
class Solution {
public int minCost(int[][] grid, int k) {
int m = grid.length, n = grid[0].length;
int inf = Integer.MAX_VALUE / 2;
int[][][] f = new int[k + 1][m][n];
for (int t = 0; t <= k; t++) {
for (int i = 0; i < m; i++) {
Arrays.fill(f[t][i], inf);
}
}
f[0][0][0] = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (i > 0) {
f[0][i][j] = Math.min(f[0][i][j], f[0][i - 1][j] + grid[i][j]);
}
if (j > 0) {
f[0][i][j] = Math.min(f[0][i][j], f[0][i][j - 1] + grid[i][j]);
}
}
}
Map<Integer, List<int[]>> g = new HashMap<>();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
int x = grid[i][j];
g.computeIfAbsent(x, z -> new ArrayList<>()).add(new int[] {i, j});
}
}
List<Integer> keys = new ArrayList<>(g.keySet());
keys.sort(Collections.reverseOrder());
for (int t = 1; t <= k; t++) {
int mn = inf;
for (int key : keys) {
List<int[]> pos = g.get(key);
for (int[] p : pos) {
mn = Math.min(mn, f[t - 1][p[0]][p[1]]);
}
for (int[] p : pos) {
f[t][p[0]][p[1]] = mn;
}
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (i > 0) {
f[t][i][j] = Math.min(f[t][i][j], f[t][i - 1][j] + grid[i][j]);
}
if (j > 0) {
f[t][i][j] = Math.min(f[t][i][j], f[t][i][j - 1] + grid[i][j]);
}
}
}
}
int ans = inf;
for (int t = 0; t <= k; t++) {
ans = Math.min(ans, f[t][m - 1][n - 1]);
}
return ans;
}
}
// Accepted solution for LeetCode #3651: Minimum Cost Path with Teleportations
func minCost(grid [][]int, k int) int {
m, n := len(grid), len(grid[0])
inf := int(^uint(0)>>1) / 4
f := make([][][]int, k+1)
for t := 0; t <= k; t++ {
f[t] = make([][]int, m)
for i := 0; i < m; i++ {
f[t][i] = make([]int, n)
for j := 0; j < n; j++ {
f[t][i][j] = inf
}
}
}
f[0][0][0] = 0
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
if i > 0 {
f[0][i][j] = min(f[0][i][j], f[0][i-1][j]+grid[i][j])
}
if j > 0 {
f[0][i][j] = min(f[0][i][j], f[0][i][j-1]+grid[i][j])
}
}
}
g := make(map[int][][]int)
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
x := grid[i][j]
g[x] = append(g[x], []int{i, j})
}
}
keys := make([]int, 0, len(g))
for key := range g {
keys = append(keys, key)
}
sort.Sort(sort.Reverse(sort.IntSlice(keys)))
for t := 1; t <= k; t++ {
mn := inf
for _, key := range keys {
pos := g[key]
for _, p := range pos {
mn = min(mn, f[t-1][p[0]][p[1]])
}
for _, p := range pos {
f[t][p[0]][p[1]] = mn
}
}
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
if i > 0 {
f[t][i][j] = min(f[t][i][j], f[t][i-1][j]+grid[i][j])
}
if j > 0 {
f[t][i][j] = min(f[t][i][j], f[t][i][j-1]+grid[i][j])
}
}
}
}
ans := inf
for t := 0; t <= k; t++ {
ans = min(ans, f[t][m-1][n-1])
}
return ans
}
# Accepted solution for LeetCode #3651: Minimum Cost Path with Teleportations
class Solution:
def minCost(self, grid: List[List[int]], k: int) -> int:
m, n = len(grid), len(grid[0])
f = [[[inf] * n for _ in range(m)] for _ in range(k + 1)]
f[0][0][0] = 0
for i in range(m):
for j in range(n):
if i:
f[0][i][j] = min(f[0][i][j], f[0][i - 1][j] + grid[i][j])
if j:
f[0][i][j] = min(f[0][i][j], f[0][i][j - 1] + grid[i][j])
g = defaultdict(list)
for i, row in enumerate(grid):
for j, x in enumerate(row):
g[x].append((i, j))
keys = sorted(g, reverse=True)
for t in range(1, k + 1):
mn = inf
for key in keys:
pos = g[key]
for i, j in pos:
mn = min(mn, f[t - 1][i][j])
for i, j in pos:
f[t][i][j] = mn
for i in range(m):
for j in range(n):
if i:
f[t][i][j] = min(f[t][i][j], f[t][i - 1][j] + grid[i][j])
if j:
f[t][i][j] = min(f[t][i][j], f[t][i][j - 1] + grid[i][j])
return min(f[t][m - 1][n - 1] for t in range(k + 1))
// Accepted solution for LeetCode #3651: Minimum Cost Path with Teleportations
use std::collections::HashMap;
impl Solution {
pub fn min_cost(grid: Vec<Vec<i32>>, k: i32) -> i32 {
let m = grid.len();
let n = grid[0].len();
let k = k as usize;
let inf: i32 = i32::MAX / 2;
let mut f = vec![vec![vec![inf; n]; m]; k + 1];
f[0][0][0] = 0;
for i in 0..m {
for j in 0..n {
if i > 0 {
f[0][i][j] = f[0][i][j].min(f[0][i - 1][j] + grid[i][j]);
}
if j > 0 {
f[0][i][j] = f[0][i][j].min(f[0][i][j - 1] + grid[i][j]);
}
}
}
let mut g: HashMap<i32, Vec<(usize, usize)>> = HashMap::new();
for i in 0..m {
for j in 0..n {
g.entry(grid[i][j]).or_default().push((i, j));
}
}
let mut keys: Vec<i32> = g.keys().cloned().collect();
keys.sort_by(|a, b| b.cmp(a));
for t in 1..=k {
let mut mn = inf;
for &key in &keys {
let pos = &g[&key];
for &(i, j) in pos {
mn = mn.min(f[t - 1][i][j]);
}
for &(i, j) in pos {
f[t][i][j] = mn;
}
}
for i in 0..m {
for j in 0..n {
if i > 0 {
f[t][i][j] = f[t][i][j].min(f[t][i - 1][j] + grid[i][j]);
}
if j > 0 {
f[t][i][j] = f[t][i][j].min(f[t][i][j - 1] + grid[i][j]);
}
}
}
}
let mut ans = inf;
for t in 0..=k {
ans = ans.min(f[t][m - 1][n - 1]);
}
ans
}
}
// Accepted solution for LeetCode #3651: Minimum Cost Path with Teleportations
function minCost(grid: number[][], k: number): number {
const [m, n] = [grid.length, grid[0].length];
const INF = 1e18;
const f: number[][][] = Array.from({ length: k + 1 }, () =>
Array.from({ length: m }, () => Array(n).fill(INF)),
);
f[0][0][0] = 0;
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
if (i > 0) f[0][i][j] = Math.min(f[0][i][j], f[0][i - 1][j] + grid[i][j]);
if (j > 0) f[0][i][j] = Math.min(f[0][i][j], f[0][i][j - 1] + grid[i][j]);
}
}
const g = new Map<number, Array<[number, number]>>();
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
const x = grid[i][j];
if (!g.has(x)) {
g.set(x, []);
}
g.get(x)!.push([i, j]);
}
}
const keys = Array.from(g.keys()).sort((a, b) => b - a);
for (let t = 1; t <= k; t++) {
let mn = INF;
for (const key of keys) {
const pos = g.get(key)!;
for (const [x, y] of pos) {
mn = Math.min(mn, f[t - 1][x][y]);
}
for (const [x, y] of pos) {
f[t][x][y] = mn;
}
}
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
if (i > 0) {
f[t][i][j] = Math.min(f[t][i][j], f[t][i - 1][j] + grid[i][j]);
}
if (j > 0) {
f[t][i][j] = Math.min(f[t][i][j], f[t][i][j - 1] + grid[i][j]);
}
}
}
}
let ans = INF;
for (let t = 0; t <= k; t++) {
ans = Math.min(ans, f[t][m - 1][n - 1]);
}
return ans;
}
Use this to step through a reusable interview workflow for this problem.
Pure recursion explores every possible choice at each step. With two choices per state (take or skip), the decision tree has 2ⁿ leaves. The recursion stack uses O(n) space. Many subproblems are recomputed exponentially many times.
Each cell in the DP table is computed exactly once from previously solved subproblems. The table dimensions determine both time and space. Look for the state variables — each unique combination of state values is one cell. Often a rolling array can reduce space by one dimension.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: An incomplete state merges distinct subproblems and caches incorrect answers.
Usually fails on: Correctness breaks on cases that differ only in hidden state.
Fix: Define state so each unique subproblem maps to one DP cell.