Luogu 题解 P4447 【[AHOI2018初中组]分组】

不同于其他的题解,二分查找的思路

所需要的变量
const int MAXN=1e5+40;
int f[MAXN],n,ans=1e9,siz[MAXN],top,q[MAXN];

变量介绍

  1. f 数组表示初始读入的数组
  2. siz 数组表示每一个组的大小(后面答案统计要用)
  3. top 组的数量
  4. 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;
}

完结撒花

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值