Flower Planting With No Adjacent
MedPrompt
You have n gardens, labeled from 1 to n, and an array paths where paths[i] = [xi, yi] describes a bidirectional path between garden xi to garden yi. In each garden, you want to plant one of 4 types of flowers.
All gardens have at most 3 paths coming into or leaving it.
Your task is to choose a flower type for each garden such that, for any two gardens connected by a path, they have different types of flowers.
Return any such a choice as an array answer, where answer[i] is the type of flower planted in the (i+1)th garden. The flower types are denoted 1, 2, 3, or 4. It is guaranteed an answer exists.
Example 1:
Input: n = 3, paths = [[1,2],[2,3],[3,1]]
Output: [1,2,3]
Explanation:
Gardens 1 and 2 have different types.
Gardens 2 and 3 have different types.
Gardens 3 and 1 have different types.
Hence, [1,2,3] is a valid answer. Other valid answers include [1,2,4], [1,4,2], and [3,2,1].Example 2:
Input: n = 4, paths = [[1,2],[3,4]]
Output: [1,2,1,2]Example 3:
Input: n = 4, paths = [[1,2],[2,3],[3,4],[4,1],[1,3],[2,4]]
Output: [1,2,3,4]
Constraints:
1 <= n <= 1040 <= paths.length <= 2 * 104paths[i].length == 21 <= xi, yi <= nxi != yi- Every garden has at most 3 paths coming into or leaving it.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach leverages the key constraint that each garden has at most 3 neighbors. It iterates through the gardens one by one and greedily assigns the first available flower type that doesn't conflict with its already-colored neighbors. Since there are 4 available flower types and at most 3 neighbors, there will always be at least one valid choice available for any garden.
Algorithm
-
- Build an adjacency list representation of the graph from the
pathsarray. Since gardens are 1-indexed, convert them to 0-indexed for array access.
- Build an adjacency list representation of the graph from the
-
- Create an
answerarray of sizento store the chosen flower type for each garden, initialized with 0s.
- Create an
-
- Iterate through each garden
ifrom 0 ton-1.
- Iterate through each garden
-
- For each garden
i, determine the colors used by its neighbors. A boolean arrayusedColorsof size 5 can be used for this.
- For each garden
-
- Iterate through the neighbors of garden
i. If a neighborjhas already been colored (i.e.,answer[j] != 0), mark its color as used (e.g.,usedColors[answer[j]] = true).
- Iterate through the neighbors of garden
-
- Iterate through the four possible flower types (1, 2, 3, 4).
-
- Assign the first flower type
cthat is not used (i.e.,!usedColors[c]) to the current gardeniby settinganswer[i] = c.
- Assign the first flower type
-
- Break the inner loop once a color is assigned, as we only need one valid color, and move to the next garden.
-
- After the main loop finishes, the
answerarray will contain a valid coloring. Return it.
- After the main loop finishes, the
Walkthrough
The problem guarantees that every garden has at most 3 neighbors. We are given 4 types of flowers (colors). This setup is a direct application of a greedy coloring algorithm. For any garden, its neighbors can occupy at most 3 distinct colors. This leaves at least one color (4 - 3 = 1) available for the current garden.
Therefore, we can simply iterate through the gardens in any order (e.g., from 1 to n) and, for each garden, pick the first color that is not being used by any of its already-colored neighbors. This process is guaranteed to succeed.
The algorithm is as follows:
- Build an adjacency list for the graph.
- Create a result array,
answer, to store the color of each garden. - Loop through each garden from
i = 0ton-1. - For each garden
i, check the colors of its neighbors. Keep track of the colors that are already taken by neighbors. - Choose the smallest-numbered color (from 1 to 4) that is not taken and assign it to garden
i.
import java.util.*; class Solution { public int[] gardenNoAdj(int n, int[][] paths) { // Adjacency list to represent the graph List<Integer>[] adj = new ArrayList[n]; for (int i = 0; i < n; i++) { adj[i] = new ArrayList<>(); } for (int[] path : paths) { // Gardens are 1-indexed, arrays are 0-indexed int u = path[0] - 1; int v = path[1] - 1; adj[u].add(v); adj[v].add(u); } // answer[i] will store the color of garden i (0-indexed) int[] answer = new int[n]; // Iterate through each garden for (int i = 0; i < n; i++) { // Use a boolean array to track used colors by neighbors boolean[] usedColors = new boolean[5]; // For colors 1, 2, 3, 4 // Check colors of adjacent gardens that are already colored for (int neighbor : adj[i]) { if (answer[neighbor] != 0) { // If neighbor is already colored usedColors[answer[neighbor]] = true; } } // Find the first unused color for (int c = 1; c <= 4; c++) { if (!usedColors[c]) { answer[i] = c; break; // Found a color, move to the next garden } } } return answer; }}Complexity
Time
O(n + E), where n is the number of gardens and E is the number of paths. Building the adjacency list takes O(n + E). The main loop runs n times. Inside the loop, we iterate over a maximum of 3 neighbors and then a maximum of 4 colors, which is constant time work for each garden. Thus, the total time complexity is O(n + E).
Space
O(n + E) to store the adjacency list, where n is the number of gardens and E is the number of paths. The `answer` array and `usedColors` array take O(n) and O(1) space respectively. The total space is dominated by the adjacency list.
Trade-offs
Pros
Very efficient and provides an optimal solution for this problem.
Simple to understand and implement.
Guaranteed to find a valid solution due to the problem's specific constraints.
Cons
This greedy strategy is not a general solution for all graph coloring problems. It works here only because of the specific problem constraints (max degree of 3 and 4 available colors).
This approach uses a standard backtracking algorithm to solve the graph coloring problem. It tries to assign a color to each garden one by one, and if an assignment leads to a conflict with its neighbors, it backtracks and tries a different color. This is a general but often inefficient method for such problems.
Algorithm
-
- Build an adjacency list to represent the garden connections.
-
- Create a
colorsarray of sizento store the flower type for each garden, initialized to 0 (uncolored).
- Create a
-
- Define a recursive function
solve(gardenIndex)that attempts to color gardens fromgardenIndexton-1.
- Define a recursive function
-
- Base Case: If
gardenIndexequalsn, all gardens are colored, so a solution has been found. Returntrue.
- Base Case: If
-
- Recursive Step: For the current
gardenIndex, iterate through the four flower types (1 to 4).
- Recursive Step: For the current
-
- For each color, check if it's valid. A color is valid if none of the adjacent gardens have this color.
-
- If the color is valid, assign it to
colors[gardenIndex]and make a recursive callsolve(gardenIndex + 1).
- If the color is valid, assign it to
-
- If the recursive call returns
true, it means a solution is found down the line, so propagatetrueup the call stack.
- If the recursive call returns
-
- If the recursive call returns
false, it means this choice of color did not lead to a solution. Backtrack by trying the next color. In a general problem, you might reset the color, but since a solution is guaranteed here, the first valid path will succeed.
- If the recursive call returns
-
- If all four colors are tried and none lead to a solution, return
false(this case won't be reached in this specific problem).
- If all four colors are tried and none lead to a solution, return
-
- Start the process by calling
solve(0).
- Start the process by calling
Walkthrough
The problem can be modeled as a graph where gardens are vertices and paths are edges. We need to color the vertices. Backtracking explores the search space of all possible color assignments recursively.
We define a function, say solve(gardenIndex), which tries to color the garden at gardenIndex. It iterates through the four possible colors. For each color, it checks if assigning it to the current garden would violate the condition (i.e., if any neighbor already has that color). If the color is valid, it's assigned, and the function recursively calls itself for the next garden, solve(gardenIndex + 1). If the recursive call eventually finds a complete, valid coloring, it returns true. If it fails, we backtrack and try the next color for the current garden. Since the problem guarantees a solution exists, this process will eventually find one.
import java.util.*; class Solution { private List<Integer>[] adj; private int[] colors; private int n; public int[] gardenNoAdj(int n, int[][] paths) { this.n = n; this.adj = new ArrayList[n]; for (int i = 0; i < n; i++) { adj[i] = new ArrayList<>(); } for (int[] path : paths) { int u = path[0] - 1; int v = path[1] - 1; adj[u].add(v); adj[v].add(u); } this.colors = new int[n]; solve(0); return colors; } private boolean solve(int gardenIndex) { if (gardenIndex == n) { return true; } for (int c = 1; c <= 4; c++) { if (isColorValid(gardenIndex, c)) { colors[gardenIndex] = c; if (solve(gardenIndex + 1)) { return true; } // Backtracking by trying the next color in the loop. // No need to reset colors[gardenIndex] = 0 because a solution is guaranteed. } } return false; // Should not be reached. } private boolean isColorValid(int gardenIndex, int color) { for (int neighbor : adj[gardenIndex]) { if (colors[neighbor] == color) { return false; } } return true; }}Complexity
Time
O(4^n * n) in the worst case for a general graph. The branching factor is 4, and the depth of recursion is n. The check for color validity takes O(degree), which is at most n. While the low degree constraint prunes the search space significantly, the exponential nature makes it too slow for the given constraints.
Space
O(n + E) for the adjacency list and O(n) for the recursion stack, where E is the number of paths. This simplifies to O(n + E).
Trade-offs
Pros
It's a general method for solving constraint satisfaction problems like graph coloring.
Guaranteed to find a solution if one exists.
Cons
Extremely inefficient for the given constraints (n up to 10^4).
Will likely result in a Time Limit Exceeded (TLE) verdict on most platforms.
Overkill for a problem with a guaranteed simple solution structure.
Solutions
Solution
class Solution {public int[] gardenNoAdj(int n, int[][] paths) { List<Integer>[] g = new List[n]; Arrays.setAll(g, k->new ArrayList<>()); for (var p : paths) { int x = p[0] - 1, y = p[1] - 1; g[x].add(y); g[y].add(x); } int[] ans = new int[n]; boolean[] used = new boolean[5]; for (int x = 0; x < n; ++x) { Arrays.fill(used, false); for (int y : g[x]) { used[ans[y]] = true; } for (int c = 1; c < 5; ++c) { if (!used[c]) { ans[x] = c; break; } } } return ans; }}class Solution {public: vector<int> gardenNoAdj(int n, vector<vector<int>> &paths) { vector<vector<int>> g(n); for (auto &p : paths) { int x = p[0] - 1, y = p[1] - 1; g[x].push_back(y); g[y].push_back(x); } vector<int> ans(n); bool used[5]; for (int x = 0; x < n; ++x) { memset(used, false, sizeof(used)); for (int y : g[x]) { used[ans[y]] = true; } for (int c = 1; c < 5; ++c) { if (!used[c]) { ans[x] = c; break; } } } return ans; }};class Solution: def gardenNoAdj(self, n: int, paths: List[List[int]]) -> List[int]: g = defaultdict(list) for x, y in paths: x, y = x - 1, y - 1 g[x]. append(y) g[y]. append(x) ans = [0] * n for x in range(n): used = {ans[y] for y in g[x]} for c in range(1, 5): if c not in used: ans[x] = c break return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.