Partition the Popcorn Packages
Autumn Tournament (2020)
Submission name: popcorn.cpp
Problem summary
N packages lie in a fixed order. Distribute them among K people so each person receives one contiguous group, possibly empty, and every package is assigned whole. Each person eats S popcorns per second. Minimize the integer number of seconds until everyone has finished.
Input
N K S, followed by N positive package sizes.
Output
The minimum completion time, rounded up to whole seconds.
Constraints
1 ≤ N,K ≤100000; 1 ≤ S ≤50; 1 ≤ package size ≤10000.
Worked example
Original statement sample.
Input
5 3 4 5 8 3 10 7
Output
4
Solution analysis
- If everyone must finish within t whole seconds, each person can receive at most t·S popcorns. Check this capacity greedily: keep adding packages to the current group until the next one would exceed it, then start a new group. A package larger than the capacity makes t impossible.
- The greedy check produces the minimum number of groups for this capacity: ending a group earlier cannot allow its successor to begin later. If at most K groups suffice, unused people receive empty groups. Feasibility is monotone because extra time only increases the capacity, so binary-search the first feasible t.
- For the sample, t=3 gives capacity 12 and needs groups[5],[8,3],[10],[7], more than three people. At t=4, capacity 16 permits[5,8,3],[10],[7], so the minimum is 4.
Why it works
Greedy always takes the longest prefix that can fit in each group. After any fixed number of groups, no other valid partition can have consumed a longer prefix; this follows inductively by comparing group endpoints. Thus its group count is minimal. Binary search returns the smallest capacity-time passing this exact feasibility test.
Watch out. Do not reorder packages or split one package between people. Use ceiling division for time bounds and long long for sums and capacity multiplication. K may exceed N because empty groups are allowed.
Commented C++17 solution
An independently written complete program with standard headers.
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
int people;
long long speed;
cin>>n>>people>>speed;
vector<int>packages(n);
long long total=0;
int largest=0;
for(int&amount:packages){
cin>>amount;
total+=amount;
largest=max(largest,amount);
}
// Greedy groups test whether everyone can finish within this time.
auto possible=[&](long long seconds){
long long capacity=seconds*speed;
long long current=0;
int groups=1;
for(int amount:packages){
if(amount>capacity){
return false;
}
if(current+amount>capacity){
groups++;
current=0;
}
current+=amount;
}
return groups<=people;
};
long long low=(largest+speed-1)/speed;
long long high=(total+speed-1)/speed;
while(low<high){
long long middle=low+(high-low)/2;
if(possible(middle)){
high=middle;
}
else{
low=middle+1;
}
}
cout<<low<<'\n';
}