Facebook Pixel

1953. Maximum Number of Weeks for Which You Can Work

Problem Description

You have n projects numbered from 0 to n - 1. Each project has a certain number of milestones to complete, given in an array milestones where milestones[i] represents the number of milestones for project i.

You need to work on these projects following two strict rules:

  1. Each week, you must complete exactly one milestone from one project (you cannot skip any week)
  2. You cannot work on the same project for two consecutive weeks

Your work stops when either:

  • All milestones from all projects are completed, OR
  • The only remaining milestones would force you to work on the same project consecutively (violating rule 2)

The goal is to find the maximum number of weeks you can work while following these rules.

For example, if you have projects with milestones [1, 2, 3], you could work for all 6 weeks by alternating between projects. But if you have [5, 1], you can only work for 3 weeks because after alternating between the two projects (completing the 1 milestone from project 2), you'd be forced to work on project 1 consecutively for the remaining milestones.

The key insight is that if one project has too many milestones compared to all others combined, it becomes impossible to complete all tasks without violating the consecutive week rule. Specifically, if the project with the most milestones (call it mx) has more than rest + 1 milestones (where rest is the sum of all other projects' milestones), then you can only complete rest * 2 + 1 weeks of work. Otherwise, you can complete all s milestones (where s is the total sum).

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

To understand this problem, let's think about the worst-case scenario. Imagine you have one project with many milestones and all other projects with very few. The constraint that you can't work on the same project consecutively means you need to alternate between projects.

Think of it like arranging items where no two identical items can be adjacent. If you have 10 red balls and only 2 blue balls, you can't arrange all of them without having red balls next to each other. The best you can do is: R-B-R-B-R, using only 5 items total.

The key realization is that the project with the maximum milestones (mx) acts as a bottleneck. To avoid working on it consecutively, we need enough "buffer" milestones from other projects to insert between its milestones.

If we denote the sum of all other projects' milestones as rest, then:

  • We can place at most rest milestones from other projects
  • Between these rest milestones, we can create rest + 1 slots for the max project's milestones
  • If mx ≤ rest + 1, we can fit all milestones of the max project without consecutive work
  • If mx > rest + 1, we can't use all milestones

When mx > rest + 1, the optimal strategy is to alternate as much as possible: work on the max project, then another project, then max project again, and so on. This gives us rest milestones from other projects, rest milestones from the max project (due to alternation), plus one final milestone from the max project, totaling rest * 2 + 1 weeks.

When mx ≤ rest + 1, we have enough variety to complete everything, so we can work for s weeks (the total sum of all milestones).

Learn more about Greedy patterns.

Solution Approach

The implementation follows a greedy approach based on our mathematical insight about the maximum project being the bottleneck.

First, we identify two key values:

  • mx: The maximum number of milestones among all projects using max(milestones)
  • s: The total sum of all milestones using sum(milestones)

From these, we calculate:

  • rest = s - mx: The sum of milestones from all projects except the one with maximum milestones

The decision logic is straightforward:

  1. Check if the maximum is too large: Compare mx with rest + 1

    • If mx > rest + 1, it means the project with the most milestones has more tasks than we can interleave with other projects
    • In this case, we can only complete rest * 2 + 1 weeks of work
    • This represents alternating between the max project and others (rest times each) plus one final milestone from the max project
  2. Otherwise, all milestones are completable:

    • If mx ≤ rest + 1, we have enough variety among projects to avoid consecutive work on any single project
    • We return s, the total number of milestones

The solution elegantly handles this with a single conditional expression:

return rest * 2 + 1 if mx > rest + 1 else s

This greedy approach works because:

  • We don't need to track which specific projects to work on each week
  • We only need to verify if a valid schedule exists based on the mathematical relationship between the maximum and the rest
  • The formula rest * 2 + 1 gives us the optimal number of weeks when we're constrained by the maximum project

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through two examples to illustrate the solution approach.

Example 1: milestones = [5, 2, 1]

First, we calculate our key values:

  • mx = 5 (the maximum milestones in any single project)
  • s = 5 + 2 + 1 = 8 (total milestones across all projects)
  • rest = s - mx = 8 - 5 = 3 (sum of milestones excluding the max project)

Now we check if mx > rest + 1:

  • Is 5 > 3 + 1?
  • Is 5 > 4? Yes!

Since the maximum project has too many milestones compared to the others, we can't complete all tasks. We can only work for rest * 2 + 1 = 3 * 2 + 1 = 7 weeks.

To visualize why: We could work like this (using A for the 5-milestone project, B for the 2-milestone project, C for the 1-milestone project):

  • Week 1: A (4 remaining in A)
  • Week 2: B (1 remaining in B)
  • Week 3: A (3 remaining in A)
  • Week 4: B (0 remaining in B)
  • Week 5: A (2 remaining in A)
  • Week 6: C (0 remaining in C)
  • Week 7: A (1 remaining in A)

At this point, we must stop because only project A has remaining milestones, and we can't work on it consecutively.

Example 2: milestones = [1, 2, 3]

Calculate our values:

  • mx = 3 (maximum milestones)
  • s = 1 + 2 + 3 = 6 (total milestones)
  • rest = s - mx = 6 - 3 = 3 (milestones excluding max project)

Check if mx > rest + 1:

  • Is 3 > 3 + 1?
  • Is 3 > 4? No!

Since we have enough variety, we can complete all milestones. Return s = 6 weeks.

A possible schedule (using A for 1-milestone, B for 2-milestone, C for 3-milestone project):

  • Week 1: C (2 remaining in C)
  • Week 2: B (1 remaining in B)
  • Week 3: C (1 remaining in C)
  • Week 4: A (0 remaining in A)
  • Week 5: C (0 remaining in C)
  • Week 6: B (0 remaining in B)

All milestones completed without violating the consecutive work rule!

Solution Implementation

1class Solution:
2    def numberOfWeeks(self, milestones: List[int]) -> int:
3        # Find the maximum value among all milestones
4        max_milestone = max(milestones)
5      
6        # Calculate the sum of all milestones
7        total_sum = sum(milestones)
8      
9        # Calculate the sum of all other milestones except the maximum one
10        sum_of_rest = total_sum - max_milestone
11      
12        # Key insight: To maximize weeks, we need to alternate between projects
13        # If the max project has more milestones than all others combined + 1,
14        # we can only complete (sum_of_rest * 2 + 1) milestones
15        # Otherwise, we can complete all milestones
16      
17        if max_milestone > sum_of_rest + 1:
18            # Can't complete all milestones from the max project
19            # We can alternate between max and others, plus one extra from max
20            return sum_of_rest * 2 + 1
21        else:
22            # Can complete all milestones by proper alternation
23            return total_sum
24
1class Solution {
2    public long numberOfWeeks(int[] milestones) {
3        // Find the maximum value and calculate the total sum
4        int maxMilestone = 0;
5        long totalSum = 0;
6      
7        // Iterate through all milestones to find max and sum
8        for (int milestone : milestones) {
9            totalSum += milestone;
10            maxMilestone = Math.max(maxMilestone, milestone);
11        }
12      
13        // Calculate the sum of all other milestones except the maximum
14        long sumOfRest = totalSum - maxMilestone;
15      
16        // If the maximum milestone is too large (more than rest + 1),
17        // we can only complete (rest * 2 + 1) milestones by alternating
18        // between the max project and others
19        // Otherwise, we can complete all milestones
20        return maxMilestone > sumOfRest + 1 ? sumOfRest * 2 + 1 : totalSum;
21    }
22}
23
1class Solution {
2public:
3    long long numberOfWeeks(vector<int>& milestones) {
4        // Find the maximum value in the milestones array
5        int maxMilestone = *max_element(milestones.begin(), milestones.end());
6      
7        // Calculate the total sum of all milestones
8        long long totalSum = accumulate(milestones.begin(), milestones.end(), 0LL);
9      
10        // Calculate the sum of all other milestones except the maximum one
11        long long sumOfRest = totalSum - maxMilestone;
12      
13        // If the maximum milestone is too large compared to the rest,
14        // we can only complete (sumOfRest * 2 + 1) milestones
15        // Otherwise, we can complete all milestones
16        return maxMilestone > sumOfRest + 1 ? sumOfRest * 2 + 1 : totalSum;
17    }
18};
19
1/**
2 * Calculates the maximum number of weeks that can be worked on projects
3 * @param milestones - Array where milestones[i] represents the number of milestones in project i
4 * @returns The maximum number of weeks that can be worked
5 */
6function numberOfWeeks(milestones: number[]): number {
7    // Find the project with the maximum number of milestones
8    const maxMilestones = Math.max(...milestones);
9  
10    // Calculate the total sum of all milestones across all projects
11    const totalMilestones = milestones.reduce((accumulator, current) => accumulator + current, 0);
12  
13    // Calculate the sum of milestones excluding the project with maximum milestones
14    const remainingMilestones = totalMilestones - maxMilestones;
15  
16    // If the maximum project has more milestones than all others combined plus 1,
17    // we can only alternate between the max project and others, limiting our weeks
18    // Otherwise, we can work on all milestones
19    return maxMilestones > remainingMilestones + 1 
20        ? remainingMilestones * 2 + 1 
21        : totalMilestones;
22}
23

Time and Space Complexity

The time complexity is O(n), where n is the number of projects (length of the milestones list). This is because:

  • Finding the maximum element using max(milestones) requires traversing all n elements once, which takes O(n) time
  • Computing the sum using sum(milestones) also requires traversing all n elements once, which takes O(n) time
  • The remaining operations (subtraction, comparison, and conditional return) are all O(1) operations
  • Therefore, the overall time complexity is O(n) + O(n) + O(1) = O(n)

The space complexity is O(1). This is because:

  • The algorithm only uses a constant amount of extra space to store variables mx, s, and rest
  • These variables store single integer values regardless of the input size
  • No additional data structures are created that scale with the input size
  • Therefore, the space complexity remains constant at O(1)

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Integer Overflow in Large Test Cases

Pitfall: When calculating the sum of milestones, the values can be very large (up to 10^5 projects with each having up to 10^9 milestones), potentially causing integer overflow in some programming languages.

Solution: In Python, this isn't an issue due to arbitrary precision integers, but in languages like Java or C++, use long/long long data types:

long long total_sum = 0;
for(int milestone : milestones) {
    total_sum += milestone;
}

2. Misunderstanding the Formula Logic

Pitfall: Developers often try to simulate the actual scheduling process or use complex dynamic programming, missing that this is purely a mathematical problem about whether a valid schedule exists.

Solution: Focus on the key insight: you only need to check if the largest project creates a bottleneck. The formula rest * 2 + 1 represents the maximum possible alternation pattern when constrained.

3. Off-by-One Error in the Comparison

Pitfall: Using mx >= rest + 1 instead of mx > rest + 1 for the condition. This would incorrectly limit the weeks when the maximum equals rest + 1, even though all milestones can be completed in this case.

Solution: Remember that when mx == rest + 1, you can still complete all milestones by ending with the max project. The correct condition is strictly greater than: mx > rest + 1.

4. Attempting to Track Actual Project Selection

Pitfall: Trying to determine which specific project to work on each week, implementing complex scheduling logic or greedy selection algorithms.

Solution: Recognize that the problem only asks for the count of weeks, not the actual schedule. The mathematical relationship between max and rest is sufficient to determine the answer without constructing the actual sequence.

5. Handling Edge Cases Incorrectly

Pitfall: Not considering special cases like single project input [5] or all zeros [0, 0, 0].

Solution: The formula naturally handles these cases:

  • Single project [5]: mx=5, rest=0, returns 0*2+1=1 (correct, can only work 1 week)
  • All zeros: mx=0, rest=0, returns 0 (correct, no work to do)
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following array represent a max heap?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More