Skip to content

Commit

Permalink
Merge pull request #130 from J-B-Mugundh/dp
Browse files Browse the repository at this point in the history
Addition of "Two City Scheduling" 3D Dynamic Programming Question
  • Loading branch information
ajay-dhangar authored Oct 7, 2024
2 parents 166ba45 + 853cc2d commit ab11483
Showing 1 changed file with 313 additions and 0 deletions.
313 changes: 313 additions & 0 deletions docs/dynamic-programming/Two-City-Scheduling-3D-DP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
---
id: two-city-scheduling-dp
title: Two City Scheduling Problem - 3D Dynamic Programming
sidebar_label: Two City Scheduling
sidebar_position: 2
description: "In this post, we'll explore the Two City Scheduling problem, a classic algorithmic challenge that can be solved efficiently using 3D Dynamic Programming. We'll delve into the problem's constraints, discuss the dynamic programming approach, and provide solutions in multiple languages such as C++, Java, Python, JavaScript, and Go. By the end, you'll understand how to use DP to minimize the total travel costs for sending an equal number of people to two different cities."
tags: [dsa, dynamic programming, scheduling]
---

## Problem Statement
You are given two cities, `A` and `B`, and a list of people, where each person has a cost associated with flying to either city. Your goal is to find the optimal way to send `N` people to city `A` and `N` people to city `B` such that the total cost is minimized.

The input consists of an array `costs` where `costs[i] = [aCost, bCost]`, which represents the cost of flying the `i-th` person to city `A` and city `B`.

### Objective
- Return the minimum total cost to send `N` people to each city.

### Example
```plaintext
Input: costs = [[10,20],[30,200],[50,30],[200,500]]
Output: 370
```


### Constraints
- `2 * n == costs.length`
- `2 <= costs.length <= 100`
- `costs.length` is even.
- `1 <= aCosti, bCosti <= 1000`

## Solution
This solution employs a dynamic programming approach to solve the Two City Scheduling problem.

### Dynamic Programming Approach

**DP Array Initialization:**

We initialize a 3D DP array `dp[i][wA][wB]`, where:
- `i` represents the first `i` people considered.
- `wA` represents the number of people sent to city `A`.
- `wB` represents the number of people sent to city `B`.

**Base Case:**

For `0` people, the total cost is `0` (no one is sent).

**Transition:**

For each person, we can choose to send them to either city `A` or city `B`, updating our DP table accordingly:
- If a person is sent to city `A`, we reduce the count of people going to city `A` and add the cost of sending them there.
- Similarly, if sent to city `B`, we reduce the count of people going to city `B` and add the respective cost.

**Final Calculation:**

The minimum total cost will be found in `dp[2 * N][N][N]`.

### Time and Space Complexity
- **Time Complexity:** O(N³), where N is half the size of the costs array.
- **Space Complexity:** O(N³) due to the 3D DP array.

### Code Implementation

C++:
```cpp
class Solution {
public:
int twoCitySchedCost(vector<vector<int>>& costs) {
int N = costs.size() / 2;
int dp[2 * N + 1][N + 1][N + 1];
for (int i = 0; i <= N; ++i) {
for (int j = 0; j <= N; ++j) {
dp[0][i][j] = 0;
}
}
for (int i = 1; i <= 2 * N; ++i) {
for (int wA = 1; wA <= N; ++wA) {
if (dp[i - 1][wA - 1][0] == INT_MAX) {
dp[i][wA][0] = INT_MAX;
} else {
dp[i][wA][0] = costs[i - 1][0] + dp[i - 1][wA - 1][0];
}
}
for (int wB = 1; wB <= N; ++wB) {
if (dp[i - 1][0][wB - 1] == INT_MAX) {
dp[i][0][wB] = INT_MAX;
} else {
dp[i][0][wB] = costs[i - 1][1] + dp[i - 1][0][wB - 1];
}
}
dp[i][0][0] = INT_MAX;
}

for (int i = 1; i <= 2 * N; ++i) {
for (int wA = 1; wA <= N; ++wA) {
for (int wB = 1; wB <= N; ++wB) {
if (dp[i - 1][wA - 1][wB] == INT_MAX) {
dp[i][wA][wB] = costs[i - 1][1];
} else if (dp[i - 1][wA][wB - 1] == INT_MAX) {
dp[i][wA][wB] = costs[i - 1][0];
} else {
dp[i][wA][wB] = min(costs[i - 1][0] + dp[i - 1][wA - 1][wB],
costs[i - 1][1] + dp[i - 1][wA][wB - 1]);
}
}
}
}
return dp[2 * N][N][N];
}
};
```
Java:
```java
class Solution {
public int twoCitySchedCost(int[][] costs) {
int N = costs.length / 2;
int[][][] dp = new int[2 * N + 1][N + 1][N + 1];
for (int i = 0; i <= N; i++) {
for (int j = 0; j <= N; j++) {
dp[0][i][j] = 0;
}
}
for (int i = 1; i <= 2 * N; i++) {
for (int wA = 1; wA <= N; wA++) {
if (dp[i - 1][wA - 1][0] == Integer.MAX_VALUE) {
dp[i][wA][0] = Integer.MAX_VALUE;
} else {
dp[i][wA][0] = costs[i - 1][0] + dp[i - 1][wA - 1][0];
}
}
for (int wB = 1; wB <= N; wB++) {
if (dp[i - 1][0][wB - 1] == Integer.MAX_VALUE) {
dp[i][0][wB] = Integer.MAX_VALUE;
} else {
dp[i][0][wB] = costs[i - 1][1] + dp[i - 1][0][wB - 1];
}
}
dp[i][0][0] = Integer.MAX_VALUE;
}
for (int i = 1; i <= 2 * N; i++) {
for (int wA = 1; wA <= N; wA++) {
for (int wB = 1; wB <= N; wB++) {
if (dp[i - 1][wA - 1][wB] == Integer.MAX_VALUE) {
dp[i][wA][wB] = costs[i - 1][1];
} else if (dp[i - 1][wA][wB - 1] == Integer.MAX_VALUE) {
dp[i][wA][wB] = costs[i - 1][0];
} else {
dp[i][wA][wB] = Math.min(costs[i - 1][0] + dp[i - 1][wA - 1][wB],
costs[i - 1][1] + dp[i - 1][wA][wB - 1]);
}
}
}
}
return dp[2 * N][N][N];
}
}
```

Python:
```python
class Solution:
def twoCitySchedCost(self, costs: List[List[int]]) -> int:
N = len(costs) // 2
dp = [[[0] * (N + 1) for _ in range(N + 1)] for _ in range(2 * N + 1)]

for i in range(1, 2 * N + 1):
for wA in range(1, N + 1):
if dp[i - 1][wA - 1][0] == float('inf'):
dp[i][wA][0] = float('inf')
else:
dp[i][wA][0] = costs[i - 1][0] + dp[i - 1][wA - 1][0]

for wB in range(1, N + 1):
if dp[i - 1][0][wB - 1] == float('inf'):
dp[i][0][wB] = float('inf')
else:
dp[i][0][wB] = costs[i - 1][1] + dp[i - 1][0][wB - 1]

dp[i][0][0] = float('inf')

for i in range(1, 2 * N + 1):
for wA in range(1, N + 1):
for wB in range(1, N + 1):
if dp[i - 1][wA - 1][wB] == float('inf'):
dp[i][wA][wB] = costs[i - 1][1]
elif dp[i - 1][wA][wB - 1] == float('inf'):
dp[i][wA][wB] = costs[i - 1][0]
else:
dp[i][wA][wB] = min(costs[i - 1][0] + dp[i - 1][wA - 1][wB],
costs[i - 1][1] + dp[i - 1][wA][wB - 1])

return dp[2 * N][N][N]
```

Javascript:
```javascript
class Solution {
twoCitySchedCost(costs) {
const N = costs.length / 2;
const dp = Array.from({ length: 2 * N + 1 }, () =>
Array.from({ length: N + 1 }, () => Array(N + 1).fill(0))
);

for (let i = 0; i <= N; i++) {
for (let j = 0; j <= N; j++) {
dp[0][i][j] = 0;
}
}

for (let i = 1; i <= 2 * N; i++) {
for (let wA = 1; wA <= N; wA++) {
dp[i][wA][0] = dp[i - 1][wA - 1][0] === Infinity ? Infinity : costs[i - 1][0] + dp[i - 1][wA - 1][0];
}
for (let wB = 1; wB <= N; wB++) {
dp[i][0][wB] = dp[i - 1][0][wB - 1] === Infinity ? Infinity : costs[i - 1][1] + dp[i - 1][0][wB - 1];
}
dp[i][0][0] = Infinity;
}

for (let i = 1; i <= 2 * N; i++) {
for (let wA = 1; wA <= N; wA++) {
for (let wB = 1; wB <= N; wB++) {
if (dp[i - 1][wA - 1][wB] === Infinity) {
dp[i][wA][wB] = costs[i - 1][1];
} else if (dp[i - 1][wA][wB - 1] === Infinity) {
dp[i][wA][wB] = costs[i - 1][0];
} else {
dp[i][wA][wB] = Math.min(costs[i - 1][0] + dp[i - 1][wA - 1][wB],
costs[i - 1][1] + dp[i - 1][wA][wB - 1]);
}
}
}
}

return dp[2 * N][N][N];
}
}
```

Go:
```go
package main

import (
"math"
)

func twoCitySchedCost(costs [][]int) int {
N := len(costs) / 2
dp := make([][][]int, 2*N+1)
for i := range dp {
dp[i] = make([][]int, N+1)
for j := range dp[i] {
dp[i][j] = make([]int, N+1)
}
}

for i := 0; i <= N; i++ {
for j := 0; j <= N; j++ {
dp[0][i][j] = 0
}
}

for i := 1; i <= 2*N; i++ {
for wA := 1; wA <= N; wA++ {
if dp[i-1][wA-1][0] == math.MaxInt {
dp[i][wA][0] = math.MaxInt
} else {
dp[i][wA][0] = costs[i-1][0] + dp[i-1][wA-1][0]
}
}
for wB := 1; wB <= N; wB++ {
if dp[i-1][0][wB-1] == math.MaxInt {
dp[i][0][wB] = math.MaxInt
} else {
dp[i][0][wB] = costs[i-1][1] + dp[i-1][0][wB-1]
}
}
dp[i][0][0] = math.MaxInt
}

for i := 1; i <= 2*N; i++ {
for wA := 1; wA <= N; wA++ {
for wB := 1; wB <= N; wB++ {
if dp[i-1][wA-1][wB] == math.MaxInt {
dp[i][wA][wB] = costs[i-1][1]
} else if dp[i-1][wA][wB-1] == math.MaxInt {
dp[i][wA][wB] = costs[i-1][0]
} else {
dp[i][wA][wB] = min(costs[i-1][0]+dp[i-1][wA-1][wB],
costs[i-1][1]+dp[i-1][wA][wB-1])
}
}
}
}

return dp[2*N][N][N]
}

func min(a, b int) int {
if a < b {
return a
}
return b
}
```

### Conclusion
This solution effectively utilizes dynamic programming to optimize the process of scheduling flights to two cities, significantly reducing the computational complexity compared to a brute-force approach. By leveraging a 3D DP array, we ensure that we only compute each state once, leading to an efficient solution.

0 comments on commit ab11483

Please sign in to comment.