Open In App

Distributing Blueberry Cheese Cake among Students

Last Updated : 13 Dec, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

There is a school in a village. It has N classes. One fine day, someone donated B blueberry cheesecakes to schools. Now you need to divide these cakes such that:

  • Each class gets at least 1 cake.
  • Each class will share the cake(s) among students.
  • Your aim is to minimize the maximum number of students per cake in any class.

Examples:

Input: N = 1, B = 2, ClassList = 35
Output: 18
Explanation: The number of students in each cake will be 17 and 18, and the maximum of them is 18.

Input: N = 2, B = 7, ClassList = 20 50
Output: 10
Explanation: The number of cakes will be 2 for the first and 5 for the second class, so there will be a maximum of 10 students for each cake.

Source: Directi Interview | Set 8 (Off-Campus)

Basic Approach: The basic way to solve the problem is as follows:

  • First, we will check whether is it possible to distribute at least 1 cake per class, distributing will not be possible only when the number of cakes is less than the number of classes, returning -1 in this scenario.
  • After that we will distribute 1 cake per class, then we will find the class that has a maximum number of students per cake and give them an extra cake to minimize it, again repeat the same process until we have no cakes left.
  • At the end, we will return the maximum number of students per cake among all classes.

Below is the implementation of the above idea.

C++




// C++ code for the above approach:
#include <bits/stdc++.h>
using namespace std;
 
int distribution(int n, int b, vector<float>& ClassList)
{
    // Check if distributing cakes is possible
    if (n > b) {
        return -1;
    }
 
    // CakeList will store the number of students
    // per cake of each class
    vector<float> CakeList = ClassList;
 
    // Initialize the number of cakes per class to 1
    vector<float> CakePerClass(n, 1);
 
    // Adjust available cakes for initial distribution
    b = b - n;
 
    // Distribute remaining cakes among classes
    while (b > 0) {
        // Decrease the number of remaining cakes
        b--;
 
        // Find the class with the maximum number of
        // students per cake of each class
        int ind
            = max_element(CakeList.begin(), CakeList.end())
            - CakeList.begin();
 
        // Distribute a cake to the selected class
        CakeList[ind]
            = ClassList[ind] / (CakePerClass[ind] + 1);
 
        // Increment the number of cakes for the class
        CakePerClass[ind]++;
    }
 
    // Return the maximum number of students per cake
    // after distributing all the cakes
    return ceil(
        *max_element(CakeList.begin(), CakeList.end()));
}
 
int main()
{
    int N = 1; // number of classes
    int B = 2; // number of blueberry cakes
    vector<float> ClassList
        = { 35 }; // number of students in each class
 
    // Call the distribution function and print the result
    cout << distribution(N, B, ClassList) << endl;
 
    return 0;
}
 
// This code is contributed by the Author


Java




import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
 
public class CakeDistribution {
 
    public static int distribution(int n, int b, List<Float> classList) {
        // Check if distributing cakes is possible
        if (n > b) {
            return -1;
        }
 
        // CakeList will store the number of students
        // per cake of each class
        List<Float> cakeList = new ArrayList<>(classList);
 
        // Initialize the number of cakes per class to 1
        List<Float> cakePerClass = new ArrayList<>(Collections.nCopies(n, 1.0f));
 
        // Adjust available cakes for initial distribution
        b = b - n;
 
        // Distribute remaining cakes among classes
        while (b > 0) {
            // Decrease the number of remaining cakes
            b--;
 
            // Find the class with the maximum number of
            // students per cake of each class
            int ind = cakeList.indexOf(Collections.max(cakeList));
 
            // Distribute a cake to the selected class
            cakeList.set(ind, classList.get(ind) / (cakePerClass.get(ind) + 1));
 
            // Increment the number of cakes for the class
            cakePerClass.set(ind, cakePerClass.get(ind) + 1);
        }
 
        // Return the maximum number of students per cake
        // after distributing all the cakes
        return (int) Math.ceil(Collections.max(cakeList));
    }
 
    public static void main(String[] args) {
        int N = 1; // number of classes
        int B = 2; // number of blueberry cakes
        List<Float> classList = new ArrayList<>();
        classList.add(35.0f); // number of students in each class
 
        // Call the distribution function and print the result
        System.out.println(distribution(N, B, classList));
    }
}
 
// This code is contributed by shivamgupta0987654321


Python3




# Python3 implementation of above approach
import math
 
 
def distribution(n, b, ClassList):
 
    # Check if distributing cakes is possible
    if n > b:
        return -1
 
    # ClassList will store number of students
    # per cake of each class
    CakeList = ClassList.copy()
 
    # Initialize the number of cakes per class to 1
    CakePerClass = [1 for i in range(n)]
 
    # Adjust available cakes for initial distribution
    b = b - n
 
    # Distribute remaining cakes among classes
    while b:
        # Decrease the number of remaining cakes
        b -= 1
 
        # Find the class with maximum number of
        # students per cake of each class
        ind = CakeList.index(max(CakeList))
 
        # Distribute a cake to the selected class
        CakeList[ind] = ClassList[ind] / (CakePerClass[ind] + 1)
 
        # Increment the number of cakes for the class
        CakePerClass[ind] += 1
 
    # Return the maximum number of students per cake
    # after distributing all the cakes
    return math.ceil(max(CakeList))
 
 
# Driver Code
N = 1  # number of classes
B = 2  # number of blueberry cakes
ClassList = [35# number of students in each class
 
# Call the distribution function and print the result
print(distribution(N, B, ClassList))
 
# This code is contributed by the Author


C#




using System;
using System.Collections.Generic;
using System.Linq;
 
public class GFG
{
    public static int Distribution(int n, int b, List<float> classList)
    {
        // Check if distributing cakes is possible
        if (n > b)
        {
            return -1;
        }
 
        // CakeList will store the number of students
        // per cake of each class
        List<float> cakeList = new List<float>(classList);
 
        // Initialize the number of cakes per class to 1
        List<float> cakePerClass = Enumerable.Repeat(1.0f, n).ToList();
 
        // Adjust available cakes for initial distribution
        b = b - n;
 
        // Distribute remaining cakes among classes
        while (b > 0)
        {
            // Decrease the number of remaining cakes
            b--;
 
            // Find the class with the maximum number of
            // students per cake of each class
            int ind = cakeList.IndexOf(cakeList.Max());
 
            // Distribute a cake to the selected class
            cakeList[ind] = classList[ind] / (cakePerClass[ind] + 1);
 
            // Increment the number of cakes for the class
            cakePerClass[ind] += 1;
        }
 
        // Return the maximum number of students per cake
        // after distributing all the cakes
        return (int)Math.Ceiling(cakeList.Max());
    }
 
    public static void Main(string[] args)
    {
        int N = 1; // number of classes
        int B = 2; // number of blueberry cakes
        List<float> classList = new List<float>();
        classList.Add(35.0f); // number of students in each class
 
        // Call the distribution function and print the result
        Console.WriteLine(Distribution(N, B, classList));
    }
}
// This code is contributed by Rohit Singh


Javascript




// JavaScript code for the above approach:
function distribution(n, b, classList) {
    // Check if distributing cakes is possible
    if (n > b) {
        return -1;
    }
 
    // CakeList will store the number of students
    // per cake of each class
    let cakeList = [...classList];
 
    // Initialize the number of cakes per class to 1
    let cakePerClass = new Array(n).fill(1);
 
    // Adjust available cakes for initial distribution
    b = b - n;
 
    // Distribute remaining cakes among classes
    while (b > 0) {
        // Decrease the number of remaining cakes
        b--;
 
        // Find the class with the maximum number of
        // students per cake of each class
        let ind = cakeList.indexOf(Math.max(...cakeList));
 
        // Distribute a cake to the selected class
        cakeList[ind] = classList[ind] / (cakePerClass[ind] + 1);
 
        // Increment the number of cakes for the class
        cakePerClass[ind]++;
    }
 
    // Return the maximum number of students per cake
    // after distributing all the cakes
    return Math.ceil(Math.max(...cakeList));
}
 
// Driver Code
 
const N = 1; // number of classes
const B = 2; // number of blueberry cakes
const ClassList = [35]; // number of students in each class
 
// Call the distribution function and print the result
console.log(distribution(N, B, ClassList));


Output

18









Time Complexity: O(N * B),

Auxiliary Space: O(N), where N is number of classes and B is number of blueberry cakes.

Efficient Approach: To solve the problem using Binary Search follow the below idea:

Instead of distributing cakes one by one, we can set a value for the “maximum students per cake” and then check if it’s possible to distribute the cakes in a way that meets this value. If we can achieve this distribution, we’ll increase the “maximum students per cake” and try again. Conversely, if we can’t achieve the desired distribution, we’ll decrease the “maximum students per cake” and attempt to find a feasible distribution.

  • To do this we can use Binary Search by setting lower bound l to 1 as in the best case scenario we are able to distribute 1 cake per child, and upper bound r to maximum student count among classes as in the worst case scenario we will have to distribute 1 cake per class.
  • Now In each iteration we will find a mid and then calculate the total number of cakes required and change the upper or lower bound accordingly.
  • To calculate the total number of cakes required for the current mid value, iterate through each ClassList and determine how many cakes are needed to accommodate the students in that class while ensuring that each cake can have at most mid students. Sum up these calculations to obtain the total cakes required.

Below is the implementation of the above idea.

C++




// C++ code for the above approach:
#include <bits/stdc++.h>
using namespace std;
 
int EfficientDistribution(int n, int b,
                        vector<int>& ClassList)
{
    // Initialize the lower bound as 1
    int l = 1;
 
    // Set the upper bound as the maximum student count
    // among classes
    int r
        = *max_element(ClassList.begin(), ClassList.end());
 
    // Check if distributing cakes is not feasible due to
    // inadequate cakes
    if (b < n) {
        return -1;
    }
 
    // Perform binary search to find the maximum students
    // per cake
    while (l < r) {
        // Calculate the middle value of the current search
        // range
        int mid = (l + r) / 2;
 
        // Initialize the total number of cakes required
        int cakes_req = 0;
 
        // Calculate the total required cakes for current
        // students per cake value
        for (int i = 0; i < n; i++) {
            cakes_req += ceil((float)ClassList[i] / mid);
        }
 
        // Check if the current distribution is feasible
        // within the given cake limit
        if (cakes_req <= b) {
            r = mid; // Update the upper bound
        }
        else {
            l = mid + 1; // Update the lower bound
        }
    }
 
    return r; // Return the maximum students per cake
}
 
// Drivers code
int main()
{
    int N = 1; // Number of classes
    int B = 2; // Number of blueberry cakes
    vector<int> ClassList
        = { 35 }; // Number of students in each class
 
    // Call the EfficientDistribution function and print the
    // result
    cout << EfficientDistribution(N, B, ClassList) << endl;
 
    return 0;
}
 
// This code is contributed by the Author


Java




import java.util.ArrayList;
import java.util.Collections;
 
public class GFG {
 
    static int efficientDistribution(int n, int b, ArrayList<Integer> classList) {
        // Initialize the lower bound as 1
        int l = 1;
 
        // Set the upper bound as the maximum student count
        // among classes
        int r = Collections.max(classList);
 
        // Check if distributing cakes is not feasible due to
        // inadequate cakes
        if (b < n) {
            return -1;
        }
 
        // Perform binary search to find the maximum students
        // per cake
        while (l < r) {
            // Calculate the middle value of the current search
            // range
            int mid = (l + r) / 2;
 
            // Initialize the total number of cakes required
            int cakesReq = 0;
 
            // Calculate the total required cakes for current
            // students per cake value
            for (int i = 0; i < n; i++) {
                cakesReq += Math.ceil((double) classList.get(i) / mid);
            }
 
            // Check if the current distribution is feasible
            // within the given cake limit
            if (cakesReq <= b) {
                r = mid; // Update the upper bound
            } else {
                l = mid + 1; // Update the lower bound
            }
        }
 
        return r; // Return the maximum students per cake
    }
 
    public static void main(String[] args) {
        int N = 1; // Number of classes
        int B = 2; // Number of blueberry cakes
        ArrayList<Integer> classList = new ArrayList<>();
        classList.add(35); // Number of students in each class
 
        // Call the efficientDistribution function and print the result
        System.out.println(efficientDistribution(N, B, classList));
    }
}
// This code is contributed by rohit singh


Python3




# Python3 implementation of above approach
import math
 
 
def EfficientDistribution(n, b, ClassList):
 
    # Initialize the lower bound as 1
    l = 1
 
    # Set the upper bound as the maximum student count among classes
    r = max(ClassList)
 
    # Check if distributing cakes is not feasible due to inadequate cakes
    if b < n:
        return "-1"
 
    # Perform binary search to find the maximum students per cake
    while l < r:
        # Calculate the middle value of the current search range
        mid = (l + r) // 2
 
        # Initialize the total number of cakes required
        cakes_req = 0
 
        # Calculate the total required cakes for current students per cake value
        for i in range(n):
            cakes_req += math.ceil(ClassList[i] / mid)
 
        # Check if the current distribution is feasible within the given cake limit
        if cakes_req <= b:
            r = mid # Update the upper bound
        else:
            l = mid + 1 # Update the lower bound
 
    return r # Return the maximum students per cake
 
 
# Driver Code
N = 1 # Number of classes
B = 2 # Number of blueberry cakes
ClassList = [35] # Number of students in each class
 
# Call the EfficientDistribution function and print the result
print(EfficientDistribution(N, B, ClassList))
 
# This code is contributed by the Author


C#




using System;
using System.Collections.Generic;
using System.Linq;
 
class Program
{
    static int EfficientDistribution(int n, int b, List<int> classList)
    {
        // Initialize the lower bound as 1
        int l = 1;
 
        // Set the upper bound as the maximum student count
        // among classes
        int r = classList.Max();
 
        // Check if distributing cakes is not feasible due to
        // inadequate cakes
        if (b < n)
        {
            return -1;
        }
 
        // Perform binary search to find the maximum students
        // per cake
        while (l < r)
        {
            // Calculate the middle value of the current search
            // range
            int mid = (l + r) / 2;
 
            // Initialize the total number of cakes required
            int cakesReq = 0;
 
            // Calculate the total required cakes for current
            // students per cake value
            foreach (int students in classList)
            {
                cakesReq += (int)Math.Ceiling((double)students / mid);
            }
 
            // Check if the current distribution is feasible
            // within the given cake limit
            if (cakesReq <= b)
            {
                r = mid; // Update the upper bound
            }
            else
            {
                l = mid + 1; // Update the lower bound
            }
        }
 
        return r; // Return the maximum students per cake
    }
 
    static void Main()
    {
        int N = 1; // Number of classes
        int B = 2; // Number of blueberry cakes
        List<int> classList = new List<int> { 35 }; // Number of students in each class
 
        // Call the EfficientDistribution method and print the result
        Console.WriteLine(EfficientDistribution(N, B, classList));
    }
}


Javascript




// Javascript code for the above approach:
 
function EfficientDistribution(n, b, ClassList) {
    // Initialize the lower bound as 1
    let l = 1;
 
    // Set the upper bound as the maximum student count
    // among classes
    let r = ClassList.reduce((a, b) => Math.max(a, b), -Infinity);
 
    // Check if distributing cakes is not feasible due to
    // inadequate cakes
    if (b < n) {
        return -1;
    }
 
    // Perform binary search to find the maximum students
    // per cake
    while (l < r) {
        // Calculate the middle value of the current search
        // range
        let mid = Math.trunc((l + r) / 2);
 
        // Initialize the total number of cakes required
        let cakes_req = 0;
 
        // Calculate the total required cakes for current
        // students per cake value
        for (let i = 0; i < n; i++) {
            cakes_req += Math.ceil(ClassList[i] / mid);
        }
 
        // Check if the current distribution is feasible
        // within the given cake limit
        if (cakes_req <= b) {
            r = mid; // Update the upper bound
        } else {
            l = mid + 1; // Update the lower bound
        }
    }
 
    return r; // Return the maximum students per cake
}
 
// Drivers code
let N = 1; // Number of classes
let B = 2; // Number of blueberry cakes
let ClassList = [35]; // Number of students in each class
 
// Call the EfficientDistribution function and print the
// result
console.log(EfficientDistribution(N, B, ClassList));
 
// This code is contributed by the ragul21


Output

18









Time Complexity: O(N * log R),

Auxiliary Space: O(N), where N is number of classes and R is maximum student count among classes.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads