There is a group of n
members, and a list of various crimes they could commit. The ith
crime generates a profit[i]
and requires group[i]
members to participate in it. If a member participates in one crime, that member can't participate in another crime.
Let's call a profitable scheme any subset of these crimes that generates at least minProfit
profit, and the total number of members participating in that subset of crimes is at most n
.
Return the number of schemes that can be chosen. Since the answer may be very large, return it modulo 109 + 7
.
Example 1:
Input: n = 5, minProfit = 3, group = [2,2], profit = [2,3] Output: 2 Explanation: To make a profit of at least 3, the group could either commit crimes 0 and 1, or just crime 1. In total, there are 2 schemes.
Example 2:
Input: n = 10, minProfit = 5, group = [2,3,5], profit = [6,7,8] Output: 7 Explanation: To make a profit of at least 5, the group could commit any crimes, as long as they commit one. There are 7 possible schemes: (0), (1), (2), (0,1), (0,2), (1,2), and (0,1,2).
Constraints:
1 <= n <= 100
0 <= minProfit <= 100
1 <= group.length <= 100
1 <= group[i] <= 100
profit.length == group.length
0 <= profit[i] <= 100
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force strategy for finding profitable schemes involves trying every possible combination of criminal activities. We want to count how many combinations are both affordable (don't require more people than we have) and profitable (reach the minimum profit target). We explore all possibilities, keeping track of the ones that satisfy our criteria.
Here's how the algorithm would work step-by-step:
def profitable_schemes_brute_force(
number_of_people, minimum_profit, group_sizes, profits
):
number_of_schemes = 0
number_of_crimes = len(group_sizes)
# Iterate through all possible combinations of crimes
for i in range(1 << number_of_crimes):
people_required = 0
profit_earned = 0
# Calculate total people and profit for the current combination
for crime_index in range(number_of_crimes):
# Check if crime is included in current combination
if (i >> crime_index) & 1:
people_required += group_sizes[crime_index]
profit_earned += profits[crime_index]
# Check if the scheme is feasible and profitable
if people_required <= number_of_people:
# Count schemes that meet the minimum profit
if profit_earned >= minimum_profit:
number_of_schemes += 1
return number_of_schemes
We want to find the number of ways to plan crimes that meet certain conditions (people required and profit generated). Instead of checking every possible combination of crimes, we use a method that builds up solutions from smaller problems. This approach remembers and reuses the results of these smaller sub-problems to avoid unnecessary recalculations, making it much faster.
Here's how the algorithm would work step-by-step:
def profitable_schemes(number_of_people, minimum_profit,
group_sizes, profits):
number_of_crimes = len(group_sizes)
modulo = 10**9 + 7
# dp[i][j][k] represents the number of schemes using first k crimes,
# with at most j people and making at least i profit.
dp = [[[0] * (number_of_crimes + 1)
for _ in range(number_of_people + 1)]
for _ in range(minimum_profit + 1)]
dp[0][0][0] = 1
for crime_index in range(1, number_of_crimes + 1):
group_size = group_sizes[crime_index - 1]
profit = profits[crime_index - 1]
for current_profit in range(minimum_profit + 1):
for people_available in range(number_of_people + 1):
# If we don't commit the crime
dp[current_profit][people_available][crime_index] =
dp[current_profit][people_available][crime_index - 1]
#If we commit the crime, check if we have enough people
if people_available >= group_size:
new_profit = min(minimum_profit,
current_profit + profit)
# Add schemes including this crime to existing schemes
dp[new_profit][people_available][crime_index] += dp[current_profit][people_available - group_size][crime_index - 1]
dp[new_profit][people_available][crime_index] %= modulo
total_schemes = 0
# Sum all schemes that meet the minimum profit requirement
for people_available in range(number_of_people + 1):
total_schemes += dp[minimum_profit][people_available][number_of_crimes]
total_schemes %= modulo
return total_schemes
Case | How to Handle |
---|---|
n = 0 (no members available) | Return 1 as no members are needed and any profit will satisfy the minProfit requirement. |
minProfit = 0 (no profit needed) | All combinations of members are valid schemes, so calculate the number of combinations possible. |
group is empty (no crime) | Return 1 as no members are participating in any crime, satisfying minProfit. |
profit is empty (no crimes) | This means all crimes yield no profit, so only consider number of people used. |
Large n and minProfit, but small group and profit sizes | The DP table should still be bounded by n and minProfit allowing for efficient calculation. |
All profits are 0 | The problem reduces to finding the number of subsets of group whose sum is less than or equal to n. |
Integer overflow in profit calculation or DP table counts | Take the modulo of the number of ways to reach a particular profit and number of people with 10^9 + 7 as specified in the problem. |
minProfit is very large, but individual profits are small. | The DP table size for profit can be optimized by clamping profit values to minProfit to avoid unnecessary calculations. |