You are given two arrays of integers, fruits and baskets, each of length
n, where fruits[i] represents the quantity of the ith type of fruit, and baskets[j] represents the capacity of the jth basket.
From left to right, place the fruits according to these rules:
Each fruit type must be placed in the leftmost available basket with a capacity greater than or equal to the quantity of that fruit type.
Each basket can hold only one type of fruit.
If a fruit type cannot be placed in any basket, it remains unplaced.
Return the number of fruit types that remain unplaced after all possible allocations are made.
Input: fruits =[3,6,1], baskets =[6,4,7]Output: 0Explanation:
*`fruits[0] = 3`is placed in`baskets[0] = 6`.*`fruits[1] = 6` cannot be placed in`baskets[1] = 4`(insufficient capacity) but can be placed in the next available basket,`baskets[2] = 7`.*`fruits[2] = 1`is placed in`baskets[1] = 4`.Since all fruits are successfully placed, we return0.
This problem extends the concept from Fruits Into Baskets II, but with significantly larger input constraints that make direct simulation inefficient. The key insight is to use square root decomposition to optimize the search process while maintaining the leftmost placement requirement.
We partition the baskets array into √n blocks of approximately equal size. Within each block, we maintain sorted order and track the maximum capacity. This allows us to quickly skip entire blocks that cannot accommodate a fruit, while still preserving the leftmost placement property within viable blocks.
import kotlin.math.*
classSolution {
funnumOfUnplacedFruits(fruits: IntArray, baskets: IntArray): Int {
val n = fruits.size
val blockSize = ceil(sqrt(n.toDouble())).toInt()
val blocks = Array(blockSize) { mutableListOf<Pair<Int, Int>>() }
// Distribute baskets into blocks
for (i in baskets.indices) {
val blockIdx = i / blockSize
blocks[blockIdx].add(Pair(baskets[i], i))
}
// Sort each block by capacity
for (block in blocks) {
block.sortBy { it.first }
}
var unplacedCount = 0for (fruitQuantity in fruits) {
var placed = falsefor (block in blocks) {
if (block.isEmpty()) continue// Skip if max capacity insufficient
if (block.last().first < fruitQuantity) continue// Find leftmost viable basket
var bestIdx = -1var minOriginalIdx = Int.MAX_VALUE
for (i in block.indices) {
val(capacity, originalIdx) = block[i]
if (capacity >= fruitQuantity && originalIdx < minOriginalIdx) {
minOriginalIdx = originalIdx
bestIdx = i
}
}
if (bestIdx != -1) {
block.removeAt(bestIdx)
placed = truebreak }
}
if (!placed) unplacedCount++ }
return unplacedCount
}
}
from math import ceil, sqrt
classSolution:
defnumOfUnplacedFruits(self, fruits: List[int], baskets: List[int]) -> int:
n = len(fruits)
# Square root decomposition: create √n blocks block_size = int(ceil(sqrt(n)))
blocks = [[] for _ in range(block_size)]
# Distribute baskets into blocks based on their original indicesfor original_idx, basket_capacity in enumerate(baskets):
block_idx = original_idx // block_size
blocks[block_idx].append((basket_capacity, original_idx))
# Sort each block by capacity to enable efficient searching# Last element in each block will have maximum capacityfor block in blocks:
block.sort()
# Process fruits and attempt placement unplaced_count =0for fruit_quantity in fruits:
placed =False# Check blocks from left to right to maintain leftmost preferencefor block in blocks:
# Skip empty blocksifnot block:
continue# Quick check: if max capacity in block < fruit quantity, skip entire block max_capacity_in_block = block[-1][0]
if max_capacity_in_block < fruit_quantity:
continue# Find the leftmost (smallest original index) basket that can fit the fruit viable_baskets = [(original_idx, capacity)
for capacity, original_idx in block
if capacity >= fruit_quantity]
if viable_baskets:
# Select basket with minimum original index (leftmost) chosen_original_idx, chosen_capacity = min(viable_baskets)
# Remove the selected basket from the block block.remove((chosen_capacity, chosen_original_idx))
placed =Truebreak# If no suitable basket found in any blockifnot placed:
unplaced_count +=1return unplaced_count
⏰ Time complexity: O(n × √n) = O(n^(3/2)), where n is the length of the arrays. Processing each fruit requires traversing at most √n blocks, and within each viable block, we perform a linear search that takes O(√n) time in the worst case.
🧺 Space complexity: O(√n), required to maintain the block structure. Each block stores at most √n baskets, and we have at most √n blocks.