Given an integer array arr[], where arr[i] denotes the number of tickets available with the i-th ticket seller.
The price of each ticket is equal to the number of tickets remaining with that seller at the time of sale. A seller can sell at most one ticket at a time, and after each sale, the price of the next ticket from that seller decreases by 1.
You are allowed to sell at most k tickets in total. Find the maximum amount that can be earned by selling the tickets.
Return the answer modulo 109+7.
Examples:
Input: arr[] = [4, 3, 6, 2, 4], k = 3
Output: 15
Explanation: One optimal sequence is to sell two tickets from the seller with 6 tickets and one ticket from a seller with 4 tickets. This gives a total earning of 6 + 5 + 4 = 15.Input: arr[] = [5, 3, 5, 2, 4, 4], k = 2
Output: 10
Explanation: One optimal sequence is to sell one ticket each from the two sellers with 5 tickets, earning 5 + 5 = 10.
Table of Content
[Naive Approach] Find Maximum Seller for Every Ticket - O(n Ă— k) Time and O(1) Space
The idea is to repeatedly scan the array to find the seller with the maximum remaining tickets, sell one ticket from that seller, decrease its ticket count by 1, and repeat this process until k tickets are sold.
#include <iostream>
#include <vector>
using namespace std;
int maxAmount(vector<int> &arr, int k)
{
const int MOD = 1000000007;
long long res = 0;
while (k--)
{
int idx = -1;
// Find seller with maximum remaining tickets.
for (int i = 0; i < arr.size(); i++)
{
if (arr[i] > 0 && (idx == -1 || arr[i] > arr[idx]))
idx = i;
}
// No tickets left.
if (idx == -1)
break;
// Sell one ticket.
res = (res + arr[idx]) % MOD;
arr[idx]--;
}
return res;
}
int main()
{
vector<int> arr = {4, 3, 6, 2, 4};
int k = 3;
cout << maxAmount(arr, k);
return 0;
}
class GFG {
static int maxAmount(int[] arr, int k)
{
final int MOD = 1000000007;
long res = 0;
while (k-- > 0) {
int idx = -1;
// Find seller with maximum remaining tickets.
for (int i = 0; i < arr.length; i++) {
if (arr[i] > 0
&& (idx == -1 || arr[i] > arr[idx]))
idx = i;
}
// No tickets left.
if (idx == -1)
break;
// Sell one ticket.
res = (res + arr[idx]) % MOD;
arr[idx]--;
}
return (int)res;
}
public static void main(String[] args)
{
int[] arr = { 4, 3, 6, 2, 4 };
int k = 3;
System.out.println(maxAmount(arr, k));
}
}
def maxAmount(arr, k):
MOD = 1000000007
res = 0
while k > 0:
k -= 1
idx = -1
# Find seller with maximum remaining tickets.
for i in range(len(arr)):
if arr[i] > 0 and (idx == -1 or arr[i] > arr[idx]):
idx = i
# No tickets left.
if idx == -1:
break
# Sell one ticket.
res = (res + arr[idx]) % MOD
arr[idx] -= 1
return res
if __name__ == '__main__':
arr = [4, 3, 6, 2, 4]
k = 3
print(maxAmount(arr, k))
using System;
class GFG {
static int maxAmount(int[] arr, int k)
{
const int MOD = 1000000007;
long res = 0;
while (k-- > 0) {
int idx = -1;
// Find seller with maximum remaining tickets.
for (int i = 0; i < arr.Length; i++) {
if (arr[i] > 0
&& (idx == -1 || arr[i] > arr[idx]))
idx = i;
}
// No tickets left.
if (idx == -1)
break;
// Sell one ticket.
res = (res + arr[idx]) % MOD;
arr[idx]--;
}
return (int)res;
}
static void Main()
{
int[] arr = { 4, 3, 6, 2, 4 };
int k = 3;
Console.WriteLine(maxAmount(arr, k));
}
}
function maxAmount(arr, k)
{
const MOD = 1000000007;
let res = 0;
while (k-- > 0) {
let idx = -1;
// Find seller with maximum remaining tickets.
for (let i = 0; i < arr.length; i++) {
if (arr[i] > 0
&& (idx === -1 || arr[i] > arr[idx]))
idx = i;
}
// No tickets left.
if (idx === -1)
break;
// Sell one ticket.
res = (res + arr[idx]) % MOD;
arr[idx]--;
}
return res;
}
// Driver Code
const arr = [ 4, 3, 6, 2, 4 ];
const k = 3;
console.log(maxAmount(arr, k));
Output
15
[Expected Approach] Greedy with Max Heap - O(n + k log n) Time and O(n) Space
The idea is to use a max heap to always select the seller with the maximum remaining tickets. After selling one ticket, decrease its count by 1 and insert it back into the heap if tickets are still available.
Working of Approach:
- Insert the ticket counts of all sellers into a max heap (priority queue) so that the seller with the maximum remaining tickets is always on top.
- Repeat the process until k tickets are sold or the heap becomes empty.
- Remove the seller with the maximum remaining tickets, add its current ticket value to the answer, and decrease its ticket count by 1.
- If the seller still has tickets remaining, insert the updated ticket count back into the max heap.
- Since the seller with the highest ticket value is always chosen first, this greedy strategy maximizes the total amount earned.
Let us understand with an example:
Input: arr[] = [4, 3, 6, 2, 4], k = 3
- Insert all ticket counts into a max heap: {6, 4, 4, 3, 2} and initialize res = 0.
- Remove 6, add it to res (res = 6), decrease it to 5, and push 5 back into the heap.
- Remove 5, add it to res (res = 11), decrease it to 4, and push 4 back into the heap.
- Remove 4, add it to res (res = 15), decrease it to 3, and push 3 back into the heap.
- Now k = 0, so stop the process and return 15.
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int maxAmount(vector<int> &arr, int k)
{
int mod = 1000000007;
int n = arr.size();
priority_queue<int> q;
// inserting values in priority queue.
for (int i = 0; i < n; i++)
q.push(arr[i]);
int res = 0, x;
// calculating answer modulo 1e9 + 7
while (k && !q.empty())
{
x = q.top();
q.pop();
res = (res + x) % mod;
x--;
k--;
if (x)
q.push(x);
}
return res;
}
int main()
{
vector<int> arr = {4, 3, 6, 2, 4};
int k = 3;
cout << maxAmount(arr, k);
return 0;
}
import java.util.Collections;
import java.util.PriorityQueue;
class GFG {
static int maxAmount(int[] arr, int k)
{
int mod = 1000000007;
int n = arr.length;
PriorityQueue<Integer> q = new PriorityQueue<>(
Collections.reverseOrder());
// inserting values in priority queue.
for (int i = 0; i < n; i++)
q.offer(arr[i]);
int res = 0, x;
// calculating answer modulo 1e9 + 7
while (k > 0 && !q.isEmpty()) {
x = q.poll();
res = (res + x) % mod;
x--;
k--;
if (x > 0)
q.offer(x);
}
return res;
}
public static void main(String[] args)
{
int[] arr = { 4, 3, 6, 2, 4 };
int k = 3;
System.out.println(maxAmount(arr, k));
}
}
import heapq
def maxAmount(arr, k):
mod = 1000000007
n = len(arr)
# Convert arr into a max heap by inverting the values
max_heap = [-x for x in arr]
heapq.heapify(max_heap)
res = 0
while k > 0 and max_heap:
# Get the largest element (smallest in the inverted max heap)
x = -heapq.heappop(max_heap)
res = (res + x) % mod
x -= 1
k -= 1
if x > 0:
# Push the updated value back into the heap
heapq.heappush(max_heap, -x)
return res
if __name__ == "__main__":
arr = [4, 3, 6, 2, 4]
k = 3
print(maxAmount(arr, k))
using System;
using System.Collections.Generic;
class GFG {
static int maxAmount(int[] arr, int k)
{
int mod = 1000000007;
int n = arr.Length;
PriorityQueue<int, int> q
= new PriorityQueue<int, int>();
// inserting values in priority queue.
for (int i = 0; i < n; i++)
q.Enqueue(arr[i], -arr[i]);
int res = 0, x;
// calculating answer modulo 1e9 + 7
while (k > 0 && q.Count > 0) {
x = q.Dequeue();
res = (res + x) % mod;
x--;
k--;
if (x > 0)
q.Enqueue(x, -x);
}
return res;
}
static void Main()
{
int[] arr = { 4, 3, 6, 2, 4 };
int k = 3;
Console.WriteLine(maxAmount(arr, k));
}
}
function createMaxHeap() {
// Creates and returns an empty max heap.
return {
heap: []
};
}
function push(heapObj, value) {
// Inserts a new value into the max heap.
heapObj.heap.push(value);
bubbleUp(heapObj, heapObj.heap.length - 1);
}
function pop(heapObj) {
// Removes and returns the maximum element from the heap.
if (heapObj.heap.length === 1) {
return heapObj.heap.pop();
}
const max = heapObj.heap[0];
heapObj.heap[0] = heapObj.heap.pop();
bubbleDown(heapObj, 0);
return max;
}
function bubbleUp(heapObj, index) {
// Restores the heap property by moving the element upward.
while (index > 0) {
let parent = Math.floor((index - 1) / 2);
if (heapObj.heap[parent] >= heapObj.heap[index]) break;
[heapObj.heap[parent], heapObj.heap[index]] =
[heapObj.heap[index], heapObj.heap[parent]];
index = parent;
}
}
function bubbleDown(heapObj, index) {
// Restores the heap property by moving the element downward.
let largest = index;
let left = 2 * index + 1;
let right = 2 * index + 2;
if (left < heapObj.heap.length &&
heapObj.heap[left] > heapObj.heap[largest]) {
largest = left;
}
if (right < heapObj.heap.length &&
heapObj.heap[right] > heapObj.heap[largest]) {
largest = right;
}
if (largest !== index) {
[heapObj.heap[index], heapObj.heap[largest]] =
[heapObj.heap[largest], heapObj.heap[index]];
bubbleDown(heapObj, largest);
}
}
function isEmpty(heapObj) {
// Returns true if the heap is empty.
return heapObj.heap.length === 0;
}
function maxAmount(arr, k) {
const MOD = 1000000007;
const heap = createMaxHeap();
// Inserting values into the max heap.
for (let x of arr) {
push(heap, x);
}
let res = 0;
// Calculating the answer modulo 1e9 + 7.
while (k > 0 && !isEmpty(heap)) {
let x = pop(heap);
res = (res + x) % MOD;
x--;
k--;
if (x > 0) {
push(heap, x);
}
}
return res;
}
// Driver code
let arr = [4, 3, 6, 2, 4];
let k = 3;
console.log(maxAmount(arr, k));
Output
15
[Alternate Approach] Binary Search with Arithmetic Progression - O(n log M) Time and O(1) Space
The idea is to binary search a ticket price threshold. After finding the threshold, all tickets having value greater than the threshold are sold directly using the arithmetic progression formula, and if required, the remaining tickets are sold at the threshold price.
Working of Approach:
- Find the ticket price threshold using Binary Search. This threshold represents the minimum ticket value above which all tickets will definitely be sold.
- For every middle value, count the number of tickets having value greater than the current threshold and adjust the search space accordingly.
- After finding the threshold, calculate the profit from all tickets above the threshold using the sum of an arithmetic progression, instead of selling them one by one.
- If fewer than k tickets are sold, sell the remaining tickets at the threshold price and add their contribution to the result.
- Finally, return the total amount earned after taking the modulo 109+7.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int maxAmount(vector<int> &arr, int k)
{
const long long MOD = 1000000007;
// Find the maximum ticket count.
int mx = *max_element(arr.begin(), arr.end());
int lo = 0, hi = mx;
// Binary search to find the selling threshold.
while (lo < hi)
{
int mid = (lo + hi) / 2;
long long cnt = 0;
// Count tickets having value greater than mid.
for (int x : arr)
{
if (x > mid)
cnt += (x - mid);
}
// Move towards a higher threshold if more than k tickets are sold.
if (cnt > k)
lo = mid + 1;
else
hi = mid;
}
int level = lo;
long long sold = 0;
long long res = 0;
// Calculate the profit from all tickets above the threshold.
for (int x : arr)
{
if (x > level)
{
long long tickets = x - level;
sold += tickets;
// Sum of arithmetic progression:
// x + (x - 1) + ... + (level + 1)
res += (1LL * (x + level + 1) * tickets) / 2;
res %= MOD;
}
}
// Sell the remaining tickets at the threshold price.
res = (res + 1LL * (k - sold) * level) % MOD;
return (int)res;
}
int main()
{
vector<int> arr = {4, 3, 6, 2, 4};
int k = 3;
cout << maxAmount(arr, k);
return 0;
}
import java.util.Arrays;
class GFG {
static int maxAmount(int[] arr, int k)
{
final long MOD = 1000000007;
// Find the maximum ticket count.
int mx = 0;
for (int x : arr)
mx = Math.max(mx, x);
int lo = 0, hi = mx;
// Binary search to find the selling threshold.
while (lo < hi) {
int mid = (lo + hi) / 2;
long cnt = 0;
// Count tickets having value greater than mid.
for (int x : arr) {
if (x > mid)
cnt += (x - mid);
}
// Move towards a higher threshold if more than
// k tickets are sold.
if (cnt > k)
lo = mid + 1;
else
hi = mid;
}
int level = lo;
long sold = 0;
long res = 0;
// Calculate the profit from all tickets above the
// threshold.
for (int x : arr) {
if (x > level) {
long tickets = x - level;
sold += tickets;
// Sum of arithmetic progression:
// x + (x - 1) + ... + (level + 1)
res += ((long)(x + level + 1) * tickets)
/ 2;
res %= MOD;
}
}
// Sell the remaining tickets at the threshold
// price.
res = (res + (long)(k - sold) * level) % MOD;
return (int)res;
}
public static void main(String[] args)
{
int[] arr = { 4, 3, 6, 2, 4 };
int k = 3;
System.out.println(maxAmount(arr, k));
}
}
def maxAmount(arr, k: int):
MOD = 1000000007
# Find the maximum ticket count.
mx = max(arr)
lo, hi = 0, mx
# Binary search to find the selling threshold.
while lo < hi:
mid = (lo + hi) // 2
cnt = 0
# Count tickets having value greater than mid.
for x in arr:
if x > mid:
cnt += (x - mid)
# Move towards a higher threshold if more than k tickets are sold.
if cnt > k:
lo = mid + 1
else:
hi = mid
level = lo
sold = 0
res = 0
# Calculate the profit from all tickets above the threshold.
for x in arr:
if x > level:
tickets = x - level
sold += tickets
# Sum of arithmetic progression:
# x + (x - 1) +... + (level + 1)
res += ((x + level + 1) * tickets) // 2
res %= MOD
# Sell the remaining tickets at the threshold price.
res = (res + (k - sold) * level) % MOD
return int(res)
if __name__ == '__main__':
arr = [4, 3, 6, 2, 4]
k = 3
print(maxAmount(arr, k))
using System;
class GFG {
static int maxAmount(int[] arr, int k)
{
const long MOD = 1000000007;
// Find the maximum ticket count.
int mx = 0;
foreach(int x in arr) mx = Math.Max(mx, x);
int lo = 0, hi = mx;
// Binary search to find the selling threshold.
while (lo < hi) {
int mid = (lo + hi) / 2;
long cnt = 0;
// Count tickets having value greater than mid.
foreach(int x in arr)
{
if (x > mid)
cnt += (x - mid);
}
// Move towards a higher threshold if more than
// k tickets are sold.
if (cnt > k)
lo = mid + 1;
else
hi = mid;
}
int level = lo;
long sold = 0;
long res = 0;
// Calculate the profit from all tickets above the
// threshold.
foreach(int x in arr)
{
if (x > level) {
long tickets = x - level;
sold += tickets;
// Sum of arithmetic progression:
// x + (x - 1) + ... + (level + 1)
res += ((long)(x + level + 1) * tickets)
/ 2;
res %= MOD;
}
}
// Sell the remaining tickets at the threshold
// price.
res = (res + (long)(k - sold) * level) % MOD;
return (int)res;
}
static void Main()
{
int[] arr = { 4, 3, 6, 2, 4 };
int k = 3;
Console.WriteLine(maxAmount(arr, k));
}
}
function maxAmount(arr, k)
{
const MOD = 1000000007;
// Find the maximum ticket count.
let mx = Math.max(...arr);
let lo = 0, hi = mx;
// Binary search to find the selling threshold.
while (lo < hi) {
let mid = Math.floor((lo + hi) / 2);
let cnt = 0;
// Count tickets having value greater than mid.
for (let x of arr) {
if (x > mid)
cnt += (x - mid);
}
// Move towards a higher threshold if more than k
// tickets are sold.
if (cnt > k)
lo = mid + 1;
else
hi = mid;
}
let level = lo;
let sold = 0;
let res = 0;
// Calculate the profit from all tickets above the
// threshold.
for (let x of arr) {
if (x > level) {
let tickets = x - level;
sold += tickets;
// Sum of arithmetic progression:
// x + (x - 1) + ... + (level + 1)
res += ((x + level + 1) * tickets) / 2;
res %= MOD;
}
}
// Sell the remaining tickets at the threshold price.
res = (res + (k - sold) * level) % MOD;
return res;
}
// Driver code
let arr = [ 4, 3, 6, 2, 4 ];
let k = 3;
console.log(maxAmount(arr, k));
Output
15