计蒜客 2019 蓝桥杯省赛 A 组模拟赛(一)I. 程序设计:人以群分

33 篇文章 0 订阅

某班有 nn 个同学,每个同学有一个外向程度 a_iai​。由于要进行某个活动,需要把他们分成若干个小组,每个小组的人数至少为 mm 人。不同外向程度的人在一个小组会产生不开心值,定义一个小组的不开心值为组内成员外向程度最大值和最小值的差,一个班级的不开心值为所有小组不开心值的最大值。

那么问题来了,如何分组使得班级的不开心值最小,请你求出这个最小的班级不开心值。

输入格式

第一行两个整数 n,mn,m,分别表示人数和每个小组最少的人数要求。

第二行 nn 个整数 a_iai​,表示每个同学的外向程度。

输出格式

一个整数,表示最小的班级不开心值。

数据范围

对于 30\%30% 的数据:1\le m \le n \le 201≤m≤n≤20,1\le a_i \le 1001≤ai​≤100。

对于 60\%60% 的数据:1\le m \le n\le 10001≤m≤n≤1000,1\le a_i \le 10001≤ai​≤1000。

对于 100\%100% 的数据:1\le m\le n \le 5\cdot10^51≤m≤n≤5⋅105,1\le a_i \le 10^91≤ai​≤109。

样例解释

第一个样例,只要每个人各自一个组,不开心值就都是 00。

第二个样例,最佳的分组情况为:9,119,11 一个组,6,3,56,3,5 一个组,两个组的不开心值分别为 22 和 33,那么班级的不开心值为 33。

样例输入1复制

5 1
2 4 6 8 10

样例输出1复制

0

样例输入2复制

5 2
9 11 6 3 5 

样例输出2复制

3

题目来源

2019 蓝桥杯省赛 A 组模拟赛(一)

地址:https://nanti.jisuanke.com/t/A2229

思路:二分+DP

先对不快乐值a[n]由小到大排序,再对答案h二分,对于二分的判断judge(h)需要用到DP,先谈谈我的做法,

dp[i]:为1表示前i个可以分组,为0表示不可分组,那么当 i-k>=m,a[i]-a[k+1]<=h时dp[k]中存在1,则dp[i]=1

dp[i]=dp[k] (a[i]-a[k+1]<=h,i-k>=m),当dp[k]存在1时则dp[i]=1,否则为0

对于dp[k]的判断可以用前缀和来避免循环查找

另一种DP思路 *【计蒜客 - 蓝桥训练】人以群分(二分 + dp)

dp[i]:表示前i个a[i]可以分组的最后一个人的编号(即编号1-dp[i]都可以成功分组)

那么对于dp[i],当编号dp[i-m]+1->a[i]间不大于h时,即a[i]-a[dp[i-m]+1]<=h时dp[i]=i;否则dp[i]=dp[i-1];

Code 1:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;

const int MAX_N=5e5+5;
int n,m;
LL a[MAX_N];
int L[MAX_N],dp[MAX_N];

bool judge(LL h);
int main()
{
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i=1;i<=n;++i)
		cin>>a[i];
	sort(a+1,a+n+1);
	LL l=0,r=1e10,h;
	while(l<=r){
		h=(l+r)/2;
		if(judge(h))	r=h-1;
		else	l=h+1;
	}
	cout<<l<<endl;
	
	return 0;
}

bool judge(LL h)
{
	memset(L,0,sizeof(L));
	memset(dp,0,sizeof(dp));
	bool boo=true;
	int l=1,r=1;
	while(r<=n){
		if(a[r]-a[l]<=h)	L[r]=l,++r;
		else	++l;
	}
	dp[0]=1;
	for(int i=1;i<=n;++i)
	{
		dp[i]=dp[i-1];
		if(i-m>=0){
			if(L[i]-2<0&&dp[i-m])	++dp[i];
			if(L[i]-2>=0&&dp[i-m]-dp[L[i]-2]>0)	++dp[i];
		}
	}
	if(dp[n]-dp[n-1]==0)	boo=false;
	return boo;
}

Code 2:

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;

const int MAX_N=5e5+5;
int n,m;
LL a[MAX_N];
int dp[MAX_N];

bool judge(LL h);
int main()
{
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i=1;i<=n;++i)
		cin>>a[i];
	sort(a+1,a+n+1);
	LL l=0,r=1e10,h;
	while(l<=r){
		h=(l+r)/2;
		if(judge(h))	r=h-1;
		else	l=h+1;
	}
	cout<<l<<endl;
	
	return 0;
}

bool judge1(LL h)
{
	dp[0]=0;
	for(int i=m;i<=n;++i)
		if(a[i]-a[dp[i-m]+1]<=h)	dp[i]=i;
		else	dp[i]=dp[i-1];
	return dp[n]==n;
}

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值