Dynamic Programming shines in linear problems, where states depend on previous elements. These are the building blocks for harder multidimensional problems.
π 1. General Template for 1D DP
When solving a linear DP problem:
-
Define state β What does
dp[i]
represent? - Base case(s) β Where does computation start?
-
Transition β How do we compute
dp[i]
using earlier states? -
Answer β Usually
dp[n-1]
ordp[n]
.
π Think of dp[i]
as: the best/optimal/possible way up to index i
.
π Category 1: Fibonacci-Type Problems
πΉ Problem: Climbing Stairs
You can take 1 or 2 steps at a time. How many ways to reach step n
?
-
State:
dp[i] = number of ways to reach step i
-
Base case:
dp[0] = 1
,dp[1] = 1
- Transition:
dp[i] = dp[i-1] + dp[i-2]
β
Java Code
public class ClimbStairs {
public static int climbStairs(int n) {
if (n <= 2) return n;
int[] dp = new int[n+1];
dp[1] = 1; dp[2] = 2;
for (int i = 3; i <= n; i++) {
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
public static void main(String[] args) {
System.out.println(climbStairs(5)); // 8
}
}
β‘ Optimization: Use 2 variables instead of full array.
π Category 2: Max Sum Problems
πΉ Problem: House Robber
You canβt rob adjacent houses. Max money?
-
State:
dp[i] = max money till house i
- Base cases:
dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])
- Transition:
dp[i] = max(dp[i-1], nums[i] + dp[i-2])
β
Java Code
public class HouseRobber {
public static int rob(int[] nums) {
if (nums.length == 1) return nums[0];
int[] dp = new int[nums.length];
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);
for (int i = 2; i < nums.length; i++) {
dp[i] = Math.max(dp[i-1], nums[i] + dp[i-2]);
}
return dp[nums.length-1];
}
public static void main(String[] args) {
int[] houses = {2,7,9,3,1};
System.out.println(rob(houses)); // 12
}
}
π Category 3: Reachability Problems
πΉ Problem: Jump Game
Given jumps at each index, can you reach last index?
-
State:
dp[i] = true if last index reachable from i
-
Transition: For each
i
, check if anyj
reachable leads totrue
.
β Greedy better, but DP works.
β
Java Code
public class JumpGame {
public static boolean canJump(int[] nums) {
boolean[] dp = new boolean[nums.length];
dp[0] = true;
for (int i = 0; i < nums.length; i++) {
if (!dp[i]) continue;
for (int j = 1; j <= nums[i] && i+j < nums.length; j++) {
dp[i+j] = true;
}
}
return dp[nums.length-1];
}
public static void main(String[] args) {
int[] nums = {2,3,1,1,4};
System.out.println(canJump(nums)); // true
}
}
π Category 4: Maximum Subarray (Kadaneβs Algorithm)
πΉ Problem: Maximum Subarray Sum
Find contiguous subarray with max sum.
-
State:
dp[i] = max sum of subarray ending at i
- Transition:
dp[i] = max(nums[i], nums[i] + dp[i-1])
β
Java Code
public class Kadane {
public static int maxSubArray(int[] nums) {
int maxSum = nums[0];
int current = nums[0];
for (int i = 1; i < nums.length; i++) {
current = Math.max(nums[i], current + nums[i]);
maxSum = Math.max(maxSum, current);
}
return maxSum;
}
public static void main(String[] args) {
int[] nums = {-2,1,-3,4,-1,2,1,-5,4};
System.out.println(maxSubArray(nums)); // 6
}
}
π Category 5: Minimum Cost Problems
πΉ Problem: Min Cost Climbing Stairs
You can take 1 or 2 steps, each with a cost. Find min cost to reach top.
-
State:
dp[i] = min cost to reach step i
- Transition:
dp[i] = cost[i] + min(dp[i-1], dp[i-2])
β
Java Code
public class MinCostClimb {
public static int minCostClimbingStairs(int[] cost) {
int n = cost.length;
int[] dp = new int[n+1];
for (int i = 2; i <= n; i++) {
dp[i] = Math.min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2]);
}
return dp[n];
}
public static void main(String[] args) {
int[] cost = {10, 15, 20};
System.out.println(minCostClimbingStairs(cost)); // 15
}
}
π Category 6: Partition Problems
πΉ Problem: Partition Equal Subset Sum
Can array be partitioned into two subsets with equal sum?
-
State:
dp[i] = whether sum i is achievable
-
Base case:
dp[0] = true
- Transition:
dp[i] = dp[i] || dp[i-num]
β
Java Code
public class PartitionSubset {
public static boolean canPartition(int[] nums) {
int sum = 0;
for (int num : nums) sum += num;
if (sum % 2 != 0) return false;
int target = sum / 2;
boolean[] dp = new boolean[target+1];
dp[0] = true;
for (int num : nums) {
for (int i = target; i >= num; i--) {
dp[i] = dp[i] || dp[i-num];
}
}
return dp[target];
}
public static void main(String[] args) {
int[] nums = {1,5,11,5};
System.out.println(canPartition(nums)); // true
}
}
β‘ Optimization Techniques in 1D DP
- Space Optimization
- Use 2 variables (
O(1)
space) for Fibonacci-like problems. - Use 1D rolling array for Knapsack.
- Prefix/Suffix Trick
- Sometimes computing prefix/suffix max/min helps avoid nested loops.
- Greedy vs DP
- Problems like Jump Game can be solved faster with greedy, but DP gives intuition.
β Common Pitfalls
- Forgetting base case initialization.
- Confusing βwaysβ vs βmin/max valueβ.
- Iterating in wrong order (important in subset/knapsack problems).
- Using 2D dp when 1D is enough.
π― Key Takeaways
- 1D DP = linear problems where
dp[i]
depends on a few previous states. - Categories: Fibonacci, Max Sum, Reachability, Min Cost, Partitioning.
- Practice moving from simple β harder: Climb Stairs β House Robber β Kadane β Partition.
- Optimize for space whenever possible.
Top comments (0)