P4447 [AHOI2018初中组]分组 题解
题目描述
小可可的学校信息组总共有n 个队员,每个人都有一个实力值a[i]a[i]。现在,一年一度的编程大赛就要到了,小可可的学校获得了若干个参赛名额,教练决定把学校信息组的nn 个队员分成若干个小组去参加这场比赛。
但是每个队员都不会愿意与实力跟自己过于悬殊的队员组队,于是要求分成的每个小组的队员实力值连续,同时,一个队不需要两个实力相同的选手。举个例子:[1, 2, 3, 4, 5][1,2,3,4,5]是合法的分组方案,因为实力值连续;[1, 2, 3, 5][1,2,3,5]不是合法的分组方案,因为实力值不连续;[0, 1, 1, 2][0,1,1,2]同样不是合法的分组方案,因为出现了两个实力值为1 的选手。
如果有小组内人数太少,就会因为时间不够而无法获得高分,于是小可可想让你给出一个合法的分组方案,满足所有人都恰好分到一个小组,使得人数最少的组人数最多,输出人数最少的组人数的最大值。
注意:实力值可能是负数,分组的数量没有限制。
输入格式
输入有两行:
第一行一个正整数n,表示队员数量。
第二行有n 个整数,第i 个整数a[i]表示第i 个队员的实力。
输出格式
输出一行,包括一个正整数,表示人数最少的组的人数最大值。
输入输出样例
输入
7
4 5 2 3 -4 -3 -5
输出
3
题解
咋一看非常简单,直接排序,然后找出连续段的个数并记录最小段的人数。提交发现wa也一片。
解决贪心问题的基本思路
1.建立数学模型来描述问题。
2.把求解的问题分成若干个子问题。
3.对每一子问题求解,得到子问题的局部最优解。
4.把子问题的局部最优解合成原来问题的一个解。
正解:
把每一个组看成一个队列。我们只要关心的是队尾每次插入的元素是什么就行。将n个数排序,从头到尾扫一遍,每次扫到一个数,就看一看现有的组(队列)中有没有末尾是该数-1的,有就插入进去,该组数量+1。直到扫完,输出最小组数的长度。每次选队列时,为了增高平均水平当然是加给最短的那个,如果没有符合要求的,新开一个队列。
#include<bits/stdc++.h>
using namespace std;
int a[100100];
int lastt[100100],lengthh[100100];
int main(){
int n;
cin>>n;
for(int i = 1;i<=n;i++) cin>>a[i];
sort(a+1,a+1+n);
int count = 0;
for(int i = 1;i<=n;i++){//遍历所有队员
bool flag = 0;
int reg = 0;
int maxn = 1000100;
for(int j = 1;j<=count;j++)//一组一组遍历所有已经分的组
if(lastt[j]==a[i]-1 && lengthh[j]<maxn)//找到可以放第i个队员的组(多组满足时,选人员最少组)
{
maxn = lengthh[j];//记录该组人数
reg = j;//记录该组序号
flag = 1;//找到标记
}
if(flag)//如果找到
{
lastt[reg] = a[i];//该组最后一个队员变为a[i]
lengthh[reg]++;//该组人数增加1
}
else//如果没有找到,建新的组
{
lastt[++count] = a[i];
lengthh[count] = 1;
}
}
int ans = 1000100;
for(int i = 1;i<=count;i++)
if(lengthh[i]<ans) ans = lengthh[i];
cout<<ans;
return 0;
}