【洛谷P4447】[AHOI2018初中组]分组
问题描述
小可可的学校信息组总共有n个队员,每个人都有一个实力值a[i]。现在,一年一度的编程大赛就要到了,小可可的学校获得了若干个参赛名额,教练决定把学校信息组的n个队员分成若干个小组去参加这场比赛。
但是每个队员都不会愿意与实力跟自己过于悬殊的队员组队,于是要求分成的每个小组的队员实力值连续,同时,一个队不需要两个实力相同的选手。举个例子:[1,2,3,4,5]是合法的分组方案,因为实力值连续;[1,2,3,5]不是合法的分组方案,因为实力值不连续;[0,1,1,2]同样不是合法的分组方案,因为出现了两个实力值为1的选手。
如果有小组内人数太少,就会因为时间不够而无法获得高分,于是小可可想让你给出一个合法的分组方案,满足所有人都恰好分到一个小组,使得人数最少的组人数最多,输出人数最少的组人数的最大值。
注意:实力值可能是负数,分组的数量没有限制。
输入格式
输入有两行:
第一行一个正整数n,表示队员数量。
第二行有n个整数,第i个整数a[i]表示第i个队员的实力。
输出格式
输出一行,包括一个正整数,表示人数最少的组的人数最大值。
样例输入
7
4 5 2 3 -4 -3 -5
样例输出
3
样例解释
分为2组,一组的队员实力值是{4, 5, 2, 3},一组是{-4, -3, -5},其中最小的组人数为3,可以发现没有比3更优的分法了。
数据范围
对于100%的数据满足:1≤n≤100000,|a[i]|≤109。
本题共10 个测试点,编号为1~10,每个测试点额外保证如下:
1~2 n≤6, 1≤a[i]≤100
3~4 n≤1000, 1≤a[i]≤105 且a[i]互不相同
5~6 n≤100000, a[i]互不相同
7~8 n≤100000, 1≤a[i]≤105
9~10 n≤100000, |a[i]|≤109
题解
把每个小组看成队,队中元素连续递增,每次在已有的队中选取满足条件的最短的队,在该队尾插入一个元素,由于要求相邻两个元素的值只差一,我们用一个结构体来维护一个队,其中las表示队尾元素的值,num表示队的长度
将读入的元素从小到大排序,每次从已有队伍中倒着扫描找到第一个队尾元素比要插入元素小一的队,插入该元素
为什么倒着扫就能找到最短的队?
首先,由于我们事先把所有元素从小到大排序,后面的元素一定不小于前面的元素
所以显然第i+1个队的队首元素一定不小于第i个队的队首元素
由题目限制知若某元素能插入一个队中,则该元素一定比该队的队尾元素大一
所以能插入该元素的队的队尾元素是相同的
而队中的元素是依次加一的
因为后面的队的队首元素不小于前面的队,所以当队尾元素相等时,后面的队一定不长于前面的队
所以从后往前倒着扫就能找到可以插入元素的最短的队
1 #include <algorithm> 2 #include <cstdio> 3 int n,a[100005],ans=2e9; 4 struct node{ 5 int num,las; 6 }s[100005]; 7 int main() 8 { 9 int i,j,k=0; 10 bool fd; 11 scanf("%d",&n); 12 for (i=1;i<=n;i++) 13 scanf("%d",&a[i]); 14 std::sort(a+1,a+n+1); 15 for (i=1;i<=n;i++) 16 { 17 fd=0; 18 for (j=k;j;j--) 19 { 20 if (s[j].las==a[i]-1) 21 { 22 fd=1; 23 s[j].num++; 24 s[j].las++; 25 break; 26 } 27 } 28 if (!fd) 29 s[++k].las=a[i], 30 s[k].num=1; 31 } 32 for (i=1;i<=k;i++) 33 if (ans>s[i].num) 34 ans=s[i].num; 35 printf("%d",ans); 36 return 0; 37 }