2019-2020 ICPC, NERC, Northern Eurasia Finals J. Just Arrange the Icons(贪心+数学)
题目链接
大意:给你一个序列,表示所有物品的种类,让你用最少的箱子装下所有物品,大小任意但需满足:
箱子中必须只能装一种数,且必须装满活着差一个装满。 物品的大小都为1。
思路:显然,我们要枚举一些大小来获得答案,那么我们用一个集合和储存合法答案,然后check所有的答案来获得一个符合所有物品的箱子大小。
初始的集合显然要从出现最少的种类获得。
主要的地方就是check是否合法了。
//p[1] 表示出现最少次数的种类数
if(p[1]%i==0)res[0][++len[0]]=i;//如果是倍数的话,显然可以
else{//否则,我们判断是否可以通过减少塞满的箱子 来 获得一个 合法的箱子
int l=p[1]/i;
int r=p[1]%i;
if(l+r>=i-1)res[0][++len[0]]=i;
}
剩下就是不断更新合法集合即可。
细节见代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e6 + 10;
#define fi first
#define se second
#define pb push_back
int t,n,a[N],cnt[N],p[N],x,res[2][N],y,len[2];
int main() {
for(scanf("%d",&t);t;t--){
scanf("%d",&n);x=0;
for(int i=1;i<=n;i++)scanf("%d",&a[i]),cnt[a[i]]++;
for(int i=1;i<=n;i++){
if(!cnt[i])continue;
p[++x]=cnt[i];
}
sort(p+1,p+1+x);len[0]=0;
for(int i=p[1]+1;i>=1;i--){
if(p[1]%i==0)res[0][++len[0]]=i;
else{
int l=p[1]/i;
int r=p[1]%i;
if(l+r>=i-1)res[0][++len[0]]=i;
}
}
int sta=0;
for(int i=2;i<=x;i++){
len[sta^1]=0;
for(int j=1;j<=len[sta];j++){
if(p[i]%res[sta][j]==0){
len[sta^1]++;
res[sta^1][len[sta^1]]=res[sta][j];
}else{
int l=p[i]/res[sta][j];
int r=p[i]%res[sta][j];
if(l+r>=res[sta][j]-1){
len[sta^1]++;
res[sta^1][len[sta^1]]=res[sta][j];
}
}
}
sta^=1;
}
int an=n,num;
for(int j=1;j<=len[sta];j++){
int re=0;num=res[sta][j];
for(int i=1;i<=x;i++){
re+=p[i]/num+(p[i]%num?1:0);
}
an=min(an,re);
}
printf("%d\n",an);
for(int i=1;i<=n;i++)cnt[a[i]]--;
}
return 0;
}