题面链接:https://ac.nowcoder.com/acm/contest/886/D
题意:给定n个物品,k个体积相等的盒子,求一个最小体积使得所有的物品都可以装到盒子里。装盒子要满足有大的就装大的,没有大的才能装小的的策略
思路:首先本题不符合单调性,对于下面这个样例
15 5
39 39 39 39 39 60 60 60 60 60 100 100 100 100 10
199 为一个合法的答案,但 200 不是,201 也不是。
最简单的方法就是暴力,从max(maxx,ceil(1.0*sum/k))一直暴力往后check找到第一个成立的即可,跑了400ms
#include
using namespace std;
typedef long long ll;
const int N=1e3+5;
const int mod=1e9+7;
int n,m,t,k,maxx,a[N];
multisetS;
multiset::iterator it;
bool check(int mid){
S.clear();
for(int i=1;i<=n;i++)S.insert(a[i]);
for(int j=1;j<=k;j++){
int sum=mid;
while(!S.empty()&&(it=S.upper_bound(sum))!=S.begin()){
it--;
sum-=*it;
S.erase(it);
}
if(S.empty())break;
}
return S.empty();
}
int main(){
scanf("%d",&t);
int cas=1;
while(t--){
scanf("%d%d",&n,&k);
int ans=0;maxx=0;
for(int i=1;i<=n;i++)scanf("%d",&a[i]),ans+=a[i],maxx=max(maxx,a[i]);
int ans2=1e6,l=maxx,r=1e6;
ans=max(maxx,(int)ceil(1.0*ans/k));
while(1){
if(check(ans)){
printf("Case #%d: %d\n",cas++,ans);
break;
}
ans++;
}
}
return 0;
}
但是本题满足假的单调性答案的分布是×××××√××××××××√√√√√√√√√√√√√,所以二分出的结果不一定对,但是二分可以找到第二个√,这样我们往前暴力几次就可以找到答案,我试了一下20次就过了,跑了80ms(膜拜咖啡鸡的思路)
#include
using namespace std;
typedef long long ll;
const int N=1e3+5;
const int mod=1e9+7;
int n,m,t,k,maxx,a[N];
multisetS;
multiset::iterator it;
bool check(int mid){
S.clear();
for(int i=1;i<=n;i++)S.insert(a[i]);
for(int j=1;j<=k;j++){
int sum=mid;
while(!S.empty()&&(it=S.upper_bound(sum))!=S.begin()){
it--;
sum-=*it;
S.erase(it);
}
if(S.empty())break;
}
return S.empty();
}
int main(){
scanf("%d",&t);
int cas=1;
while(t--){
scanf("%d%d",&n,&k);
int ans=0;maxx=0;
for(int i=1;i<=n;i++)scanf("%d",&a[i]),ans+=a[i],maxx=max(maxx,a[i]);
int ans2=1e6,l=maxx,r=1e6;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)){
ans2=mid;
r=mid-1;
}
else l=mid+1;
}
ans=max(maxx,(int)ceil(1.0*ans/k));
int ji=1;
for(int i=1;i<=20&&ans2-i>=ans;i++){
if(check(ans2-i)){
printf("Case #%d: %d\n",cas++,ans2-i);
ji=0;
break;
}
}
if(ji)printf("Case #%d: %d\n",cas++,ans2);
}
return 0;
}