Open In App

Maximizing Toy Purchases with Budget Constraints and Broken Toys

Last Updated : 10 Jan, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Given N toys in a shop. The cost of each toy is represented by an array A[]. You are given Q queries, For ith query, you have a C amount of money which you can use to purchase the toys. Also, there are K broken toys and you won’t purchase them. The task is to calculate the maximum number of toys you can purchase using the C amount of money. (1-based indexing is used. Each query is treated independently).

Query definition:

  • The first element represents an integer C where C=Queries[i][0].
  • The second element represents an integer K, where K = Queries[i][1].
  • The next K integers represent the indices of broken toys which are Queries[i][j], j>1

Examples:

Input: N = 5, A[] = {8, 6, 9, 2, 5}, Q = 2, Query[][] = {{12, 2, 3, 4}, {30, 0}}
Output: 2 5
Explanation: Query 1: C = 12, K = 2,
Indices of Broken toys is {3, 4}.
Indices of Available toys are {1, 2, 5}. If we purchase the toys 2 and 5, then cost = A[2] + A[5] = 6 + 5 = 11, Therefore,We purchase the 2 toys using 11 amount of money.
Query 2: C = 30, K = 0.
There is no broken toy. We can purchase all toys, cost = A[1] + A[2] + A[3] + A[4] + A[5] = 30, Therefore,We purchase the 5 toys using 30 amount of money.

Input: N = 2, A[] = {3, 3}, Q = 1, Query[][] = {{1, 0}}
Output: 0
Explanation: Query 1: C = 1, K = 0 , There is no broken toy. We have not enough amount to purchase any toy.

Approach: To solve the problem follow the below idea:

The idea is to use binary indexed trees (Fenwick trees) to efficiently solve queries on a set of bowls. It calculates sums, tracks frequencies, and adjusts values for discarded bowls in each query. The binary search identifies the position where the sum of values exceeds a limit, considering frequencies. The process is repeated for all queries, and the results are stored in a vector, which is returned as the final answer.

Step-by-step approch:

  • Initialise two binary indexed tree or fenwick tree, b1 and b2. b1 will give the sum of the array while b2 will provide details about frequency.
  • In each query, start iterating for all the discarded k bowls and update them as their negation in b1 and as -1 in b2.
  • Use binary search to find the first position after which sum >= C (limit given). Simply add the sum of frequency till pos-1 in the answer, and for the remaining, add the minimum of (C – sum till (pos-1)) / pos and the frequency of pos.
  • Update back to all k discarded bowls to their original value in both trees.
  • Repeat the above for all queries and push all answers into a vector, res.
  • Return res.

Below is the implementation of the above approach:

C++




#include <iostream>
#include <vector>
using namespace std;
 
const int N = 1e6 + 5;
int fr[N]; // array to store the frequency of elements
 
class tree {
public:
    vector<long long> bit; // Binary Indexed Tree
    tree()
    {
        bit = vector<long long>(N); // initialize BIT
    }
    void add(int node, int v)
    {
        for (; node < N; node += (node & (-node)))
            bit[node] += v; // updating BIT by adding v to
                            // each element
    }
    long long get(int node)
    {
        long long x = 0;
        for (; node > 0; node -= (node & (-node)))
            x += bit[node]; // sum of elements in BIT
        return x;
    }
};
tree obj1, obj2; // creating two instances of tree
 
vector<int> maximumToys(int N, vector<int> A, int Q,
                        vector<vector<int> > Queries)
{
    vector<int> res;
 
    for (auto i : A) {
        fr[i]++; // counting the frequency of each element
                // in A array
    }
 
    for (int i = 0; i < A.size(); i++) {
        obj1.add(A[i], A[i]); // adding elements to bit1
        obj2.add(A[i],
                1); // adding 1 to bit2 for each element
    }
 
    for (auto i : Queries) {
        long long C = i[0]; // value of C
        for (int j = 2; j < i.size(); j++) {
            obj1.add(A[i[j] - 1],
                    -A[i[j] - 1]); // removing the element
                                    // from bit1
            obj2.add(A[i[j] - 1],
                    -1); // decrementing 1 from bit2
        }
 
        long long lw = 1,
                hg = 1e6; // lower bound and higher bound
        long long pos = 1e6; // position
        while (lw <= hg) {
            int mid = (lw + hg) / 2;
            if (obj1.get(mid)
                >= C) { // check if the sum of elements till
                        // mid is greater or equal to C
                pos = mid;
                hg = mid - 1;
            }
            else {
                lw = mid + 1;
            }
        }
 
        long long ans = obj2.get(
            pos
            - 1); // get the sum till position-1 from bit2
        long long mx = min(
            (C - obj1.get(pos - 1)) / pos,
            (long long)
                fr[pos]); // find the maximum possible sum
        ans += mx; // add the maximum sum to ans
        res.push_back(ans); // push the answer to res
        for (int j = 2; j < i.size(); j++) {
            obj1.add(A[i[j] - 1],
                    A[i[j] - 1]); // add the element back
                                // to bit1
            obj2.add(A[i[j] - 1], 1); // add 1 to bit2
        }
    }
    for (int i = 0; i < A.size(); i++) {
        obj1.add(A[i],
                -A[i]); // remove the element from bit1
        obj2.add(A[i], -1); // decrement 1 from bit2
        fr[A[i]]--; // decrement the frequency of the
                    // element in fr array
    }
    return res; // return the result
}
 
int main()
{
 
    vector<int> A = { 8, 6, 9, 2, 5 };
    int Q = 2, N = 5;
    vector<vector<int> > Queries
        = { { 12, 2, 3, 4 }, { 30, 0 } };
 
    vector<int> result = maximumToys(N, A, Q, Queries);
 
    for (int val : result) {
        cout << val << " ";
    }
    cout << endl;
 
    return 0;
}


Java




import java.util.ArrayList;
import java.util.List;
 
class Main {
    static final int N = 1000005;
    static int[] fr = new int[N]; // array to store the
                                  // frequency of elements
 
    static class Tree {
        List<Long> bit; // Binary Indexed Tree
 
        Tree()
        {
            bit = new ArrayList<>(N); // initialize BIT
            for (int i = 0; i < N; i++) {
                bit.add(0L);
            }
        }
 
        void add(int node, int v)
        {
            for (; node < N; node += (node & (-node))) {
                bit.set(node,
                        bit.get(node)
                            + v); // updating BIT by adding
                                  // v to each element
            }
        }
 
        long get(int node)
        {
            long x = 0;
            for (; node > 0; node -= (node & (-node))) {
                x += bit.get(
                    node); // sum of elements in BIT
            }
            return x;
        }
    }
 
    static Tree obj1 = new Tree();
    static Tree obj2
        = new Tree(); // creating two instances of Tree
 
    static List<Integer>
    maximumToys(int N, List<Integer> A, int Q,
                List<List<Integer> > Queries)
    {
        List<Integer> res = new ArrayList<>();
 
        for (int i : A) {
            fr[i]++; // counting the frequency of each
                     // element in A array
        }
 
        for (int i = 0; i < A.size(); i++) {
            obj1.add(A.get(i),
                     A.get(i)); // adding elements to bit1
            obj2.add(
                A.get(i),
                1); // adding 1 to bit2 for each element
        }
 
        for (List<Integer> i : Queries) {
            long C = i.get(0); // value of C
            for (int j = 2; j < i.size(); j++) {
                obj1.add(A.get(i.get(j) - 1),
                         -A.get(i.get(j)
                                - 1)); // removing the
                                       // element from bit1
                obj2.add(A.get(i.get(j) - 1),
                         -1); // decrementing 1 from bit2
            }
 
            long lw = 1,
                 hg
                 = 1000000; // lower bound and higher bound
            long pos = 1000000; // position
            while (lw <= hg) {
                int mid = (int)((lw + hg) / 2);
                if (obj1.get(mid)
                    >= C) { // check if the sum of elements
                            // till mid is greater or equal
                            // to C
                    pos = mid;
                    hg = mid - 1;
                }
                else {
                    lw = mid + 1;
                }
            }
 
            long ans = obj2.get(
                (int)(pos - 1)); // get the sum till
                                 // position-1 from bit2
            long mx = Math.min(
                (C - obj1.get((int)(pos - 1))) / pos,
                (long)fr[(int)pos]); // find the maximum
                                     // possible sum
            ans += mx; // add the maximum sum to ans
            res.add((int)ans); // push the answer to res
            for (int j = 2; j < i.size(); j++) {
                obj1.add(
                    A.get(i.get(j) - 1),
                    A.get(i.get(j) - 1)); // add the element
                                          // back to bit1
                obj2.add(A.get(i.get(j) - 1),
                         1); // add 1 to bit2
            }
        }
 
        for (int i = 0; i < A.size(); i++) {
            obj1.add(
                A.get(i),
                -A.get(i)); // remove the element from bit1
            obj2.add(A.get(i), -1); // decrement 1 from bit2
            fr[A.get(i)]--; // decrement the frequency of
                            // the element in fr array
        }
        return res; // return the result
    }
 
    public static void main(String[] args)
    {
        List<Integer> A = List.of(8, 6, 9, 2, 5);
        int Q = 2, N = 5;
        List<List<Integer> > Queries
            = List.of(List.of(12, 2, 3, 4), List.of(30, 0));
 
        List<Integer> result
            = maximumToys(N, A, Q, Queries);
 
        for (int val : result) {
            System.out.print(val + " ");
        }
        System.out.println();
    }
}


Python3




class Tree:
    def __init__(self):
        self.bit = [0] * (10 ** 6 + 5)
 
    def add(self, node, v):
        # Update the Binary Indexed Tree (BIT) by adding v to each element
        while node < len(self.bit):
            self.bit[node] += v
            node += node & -node
 
    def get(self, node):
        # Get the sum of elements in BIT up to the given node
        x = 0
        while node > 0:
            x += self.bit[node]
            node -= node & -node
        return x
 
 
def maximum_toys(N, A, Q, Queries):
    res = []
    fr = [0] * (10 ** 6 + 5)
    obj1 = Tree()
    obj2 = Tree()
 
    # Count the frequency of each element in array A
    for i in A:
        fr[i] += 1
 
    # Build the initial Binary Indexed Trees (BITs)
    for i in A:
        obj1.add(i, i)
        obj2.add(i, 1)
 
    # Process each query in Queries
    for i in Queries:
        C = i[0# Value of C
        # Update BITs by removing elements specified in the query
        for j in i[2:]:
            obj1.add(A[j - 1], -A[j - 1])
            obj2.add(A[j - 1], -1)
 
        # Binary search to find the position where the sum is greater or equal to C
        lw, hg = 1, 10 ** 6
        pos = 10 ** 6
        while lw <= hg:
            mid = (lw + hg) // 2
            if obj1.get(mid) >= C:
                pos = mid
                hg = mid - 1
            else:
                lw = mid + 1
 
        # Calculate the maximum possible sum and update the result
        ans = obj2.get(pos - 1)
        mx = min((C - obj1.get(pos - 1)) // pos, fr[pos])
        ans += mx
        res.append(ans)
 
        # Update BITs by adding back the removed elements
        for j in i[2:]:
            obj1.add(A[j - 1], A[j - 1])
            obj2.add(A[j - 1], 1)
 
    # Cleanup: Remove elements from BITs and decrement frequencies
    for i in A:
        obj1.add(i, -i)
        obj2.add(i, -1)
        fr[i] -= 1
 
    return res
 
 
if __name__ == "__main__":
    # Example input
    A = [8, 6, 9, 2, 5]
    Q = 2
    N = 5
    Queries = [[12, 2, 3, 4], [30, 0]]
 
    # Get and print the result
    result = maximum_toys(N, A, Q, Queries)
    print(" ".join(map(str, result)))


C#




using System;
using System.Collections.Generic;
 
class MainClass {
    const int N = 1000005;
    static int[] fr = new int[N]; // array to store the
                                  // frequency of elements
 
    class Tree {
        List<long> bit; // Binary Indexed Tree
 
        public Tree()
        {
            bit = new List<long>(N); // initialize BIT
            for (int i = 0; i < N; i++) {
                bit.Add(0L);
            }
        }
 
        public void Add(int node, int v)
        {
            for (; node < N; node += (node & (-node))) {
                bit[node] += v; // updating BIT by adding v
                                // to each element
            }
        }
 
        public long Get(int node)
        {
            long x = 0;
            for (; node > 0; node -= (node & (-node))) {
                x += bit[node]; // sum of elements in BIT
            }
            return x;
        }
    }
 
    static Tree obj1 = new Tree();
    static Tree obj2
        = new Tree(); // creating two instances of Tree
 
    static List<int> MaximumToys(int N, List<int> A, int Q,
                                 List<List<int> > Queries)
    {
        List<int> res = new List<int>();
 
        foreach(int i in A)
        {
            fr[i]++; // counting the frequency of each
                     // element in A array
        }
 
        for (int i = 0; i < A.Count; i++) {
            obj1.Add(A[i], A[i]); // adding elements to bit1
            obj2.Add(
                A[i],
                1); // adding 1 to bit2 for each element
        }
 
        foreach(List<int> query in Queries)
        {
            long C = query[0]; // value of C
            for (int j = 2; j < query.Count; j++) {
                obj1.Add(
                    A[query[j] - 1],
                    -A[query[j] - 1]); // removing the
                                       // element from bit1
                obj2.Add(A[query[j] - 1],
                         -1); // decrementing 1 from bit2
            }
 
            long lw = 1,
                 hg
                 = 1000000; // lower bound and higher bound
            long pos = 1000000; // position
            while (lw <= hg) {
                int mid = (int)((lw + hg) / 2);
                if (obj1.Get(mid)
                    >= C) // check if the sum of elements
                          // till mid is greater or equal to
                          // C
                {
                    pos = mid;
                    hg = mid - 1;
                }
                else {
                    lw = mid + 1;
                }
            }
 
            long ans = obj2.Get(
                (int)(pos - 1)); // get the sum till
                                 // position-1 from bit2
            long mx = Math.Min(
                (C - obj1.Get((int)(pos - 1))) / pos,
                fr[(int)pos]); // find the maximum possible
                               // sum
            ans += mx; // add the maximum sum to ans
            res.Add((int)ans); // push the answer to res
            for (int j = 2; j < query.Count; j++) {
                obj1.Add(
                    A[query[j] - 1],
                    A[query[j]
                      - 1]); // add the element back to bit1
                obj2.Add(A[query[j] - 1],
                         1); // add 1 to bit2
            }
        }
 
        for (int i = 0; i < A.Count; i++) {
            obj1.Add(A[i],
                     -A[i]); // remove the element from bit1
            obj2.Add(A[i], -1); // decrement 1 from bit2
            fr[A[i]]--; // decrement the frequency of the
                        // element in fr array
        }
        return res; // return the result
    }
 
    public static void Main(string[] args)
    {
        List<int> A = new List<int>{ 8, 6, 9, 2, 5 };
        int Q = 2, N = 5;
        List<List<int> > Queries = new List<List<int> >{
            new List<int>{ 12, 2, 3, 4 },
            new List<int>{ 30, 0 }
        };
 
        List<int> result = MaximumToys(N, A, Q, Queries);
 
        foreach(int val in result)
        {
            Console.Write(val + " ");
        }
        Console.WriteLine();
    }
}


Javascript




// Define a class for Binary Indexed Tree (BIT)
class Tree {
  constructor() {
    // Initialize the BIT array with zeros
    this.bit = new Array(1000005).fill(0);
  }
 
  // Method to update the BIT by adding a value to each element
  add(node, v) {
    while (node < this.bit.length) {
      this.bit[node] += v;
      node += node & -node;
    }
  }
 
  // Method to get the sum of elements in BIT up to the given node
  get(node) {
    let x = 0;
    while (node > 0) {
      x += this.bit[node];
      node -= node & -node;
    }
    return x;
  }
}
 
// Function to solve the problem
function maximumToys(N, A, Q, Queries) {
  // Initialize an array to store the results
  const res = [];
  // Initialize an array to keep track of element frequencies
  const fr = new Array(1000005).fill(0);
  // Create instances of the Tree class for two BITs
  const obj1 = new Tree();
  const obj2 = new Tree();
 
  // Count the frequency of each element in array A
  for (let i = 0; i < A.length; i++) {
    fr[A[i]] += 1;
  }
 
  // Build the initial Binary Indexed Trees (BITs)
  for (let i = 0; i < A.length; i++) {
    obj1.add(A[i], A[i]);
    obj2.add(A[i], 1);
  }
 
  // Process each query in Queries
  for (let i = 0; i < Q; i++) {
    // Extract the value of C from the current query
    const C = Queries[i][0];
     
    // Update BITs by removing elements specified in the query
    for (let j = 2; j < Queries[i].length; j++) {
      obj1.add(A[Queries[i][j] - 1], -A[Queries[i][j] - 1]);
      obj2.add(A[Queries[i][j] - 1], -1);
    }
 
    // Binary search to find the position where the sum is greater or equal to C
    let lw = 1, hg = 1000000;
    let pos = 1000000;
    while (lw <= hg) {
      const mid = Math.floor((lw + hg) / 2);
      if (obj1.get(mid) >= C) {
        pos = mid;
        hg = mid - 1;
      } else {
        lw = mid + 1;
      }
    }
 
    // Calculate the maximum possible sum and update the result
    const ans = obj2.get(pos - 1);
    const mx = Math.min((C - obj1.get(pos - 1)) / pos | 0, fr[pos]);
    res.push(ans + mx);
 
    // Update BITs by adding back the removed elements
    for (let j = 2; j < Queries[i].length; j++) {
      obj1.add(A[Queries[i][j] - 1], A[Queries[i][j] - 1]);
      obj2.add(A[Queries[i][j] - 1], 1);
    }
  }
 
  // Cleanup: Remove elements from BITs and decrement frequencies
  for (let i = 0; i < A.length; i++) {
    obj1.add(A[i], -A[i]);
    obj2.add(A[i], -1);
    fr[A[i]] -= 1;
  }
 
  // Return the final results
  return res;
}
 
// Example input
const A = [8, 6, 9, 2, 5];
const Q = 2;
const N = 5;
const Queries = [
  [12, 2, 3, 4],
  [30, 0]
];
 
// Get and print the result
const result = maximumToys(N, A, Q, Queries);
console.log(result.join(" "));


Output

2 5 












Time Complexity: O(NLogMx + Q*K*LogMx + Q*(LogMx)^2)
Auxiliary Space: O(Mx), Where Mx is the maximum element present in the array A[i].



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads