不同于其他的题解,二分查找的思路
所需要的变量
const int MAXN=1e5+40;
int f[MAXN],n,ans=1e9,siz[MAXN],top,q[MAXN];
变量介绍
- f 数组表示初始读入的数组
- siz 数组表示每一个组的大小(后面答案统计要用)
- top 组的数量
- q 数组(看第二部分)
第二部分 q数组
q[x]表示第x组要想继续添加成员所需要的实力值
因为排过序,很显然每次加入一个成员后要将q[该成员所在组]++,
这样可以继续添加和他连号即比它大的成员
第三部分 二分查找
每次直接在q数组中查找与当前成员实力值相等的组.
(维护单调性请看第四部分)
找到后
1.一定要判断是否和当前成员实力值相等
相等则如第一部分更新q数组,否则在q中就找不到该值,需要开一个新的组。
2.判断是否超出top(即组数)
因为lower_bound查找的缘故,查到比q数组里任意一个数都大的数时,就会返回top+1,
因为本题0为有效状态,所以不能像 1 一样判断,需要特判。
第四部分
那大家想问了
while(q[pos+1]==f[i]&&pos<top) pos++;
这句话干嘛的呢???
这是为了维护q数组的单调性,每次对相等的最后一个更新
并且我们会发现 因为我们排过序,所以每次如果q数组里有相等的数,
会发现下标越大,该组的大小越小。
因此对最后一个更新。
咦?这个语句好像还正好使最小的组最大这个条件得到了维护呢QwQ。
一箭双雕
至此本题就解决了~~~~
呈上AC代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=1e5+40;
int f[MAXN],n,ans=1e9,siz[MAXN],top,q[MAXN];
signed main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&f[i]);
sort(f+1,f+n+1);
for(int i=1;i<=n;i++){
int pos=lower_bound(q+1,q+top+1,f[i])-q; //查找当前成员应该放在那一组
while(q[pos+1]==f[i]&&pos<top) pos++; //一直找到相等的最后一个
if(pos>top||q[pos]!=f[i]) siz[++top]=1,q[top]=f[i]+1; //无法更新,重开一个组
else siz[pos]++,q[pos]++; //对当前组更新
}
for(int i=1;i<=top;i++) ans=min(ans,siz[i]); //对所有组取最小值
printf("%d\n",ans);
return 0;
}
超短程序QwQ
后序
本部分为爱思考的Oier们准备
再贴一个用手写的二分的做法,此做法虽然免除了上面STL做法的第三部分
但是也会出锅:就是查找时如果实力值小于q中所有的数,就会查找到q[0],还是一句话0为有效状态,所以我们把q[0]还要赋值为1e9+1。
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=1e5+40;
int f[MAXN],n,ans=1e9,siz[MAXN],top,q[MAXN];
signed main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&f[i]);
sort(f+1,f+n+1); q[0]=1e9+1;
for(int i=1;i<=n;i++){
int l=0,r=top;
while(l<r){
int mid=(l+r+1)>>1;
if(f[i]>=q[mid]) l=mid;
else r=mid-1;
}
if(q[l]!=f[i]) siz[++top]=1,q[top]=f[i]+1;
else siz[l]++,q[l]++;
}
for(int i=1;i<=top;i++) ans=min(ans,siz[i]);
printf("%d\n",ans);
return 0;
}