题目分析:要选出最小分组的最大值,而不是要选出最小的一个分组,那样的话答案必然只会是1
所以我们要尽可能的让队伍组成,又要减少孤立点的出现
贪心:在所以可以插入的队伍中,选择队伍长度最小的插入
2.ac代码
贪心+队列
贪心+队列时间复杂度是O(nm)
m是队伍组数
其实就是在所有的组数中,寻找满足两个条件的组
1.队长最小
2.可以插入(上一个插入的数==当前数-1)
贪心+队列处理方法其实就是区间分组的处理方法,不过这里不需要维护一个右端点,只需要找到一个能力值==当前能力-1的队列就可以了,所以相对来说比较简单
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1e5 + 10;
typedef long long ll;
int a[N];
int q[N]; //记录该队最后一个人的能力值
int cnt = 0;
int len[N];//记录该队的长度
int main()
{
int n;
cin >> n;
for (int i = 0;i < n;i++)
cin >> a[i];
sort(a, a + n);
for (int i = 0;i < n;i++)
{
int x = a[i];
int v = -1;
int minl = 1e9;
//每次寻找一个满足条件的长度最小的队入队
for (int j = 0;j < cnt;j++)
{
if (q[j] == x - 1&&minl>len[j])
{
v = j;
minl = len[j];
}
}
if (v == -1) //没找到,新开一队
q[cnt] = x, len[cnt++] = 1;
else //找到了,进队
q[v] = x, len[v]++;
}
int minl = 1e9;
for (int i = 0;i < cnt;i++)
minl = min(minl, len[i]);
cout << minl;
}
二分查找+贪心
O(n*logm) m表示组数
实质上是对贪心算法的优化
二分查找+贪心是本题最优解
就是把寻找组数的过程运用二分查找优化
对于一群可以插入的队列,优先插入最后一个(这样子就能够保证单调性,因为二分查找的前提是数组具有单调性)
#include <iostream>
#include <algorithm>
#include <map>
using namespace std;
const int N = 100005;
int a[N];
int q[N];
int siz[N];
int n;
int cnt=0;
int binary_search(int x)
{//寻找最后一个能插入的点
int l = 0, r = cnt-1;
while (l < r)
{
int mid = l + r +1 >> 1;
if (q[mid] <= x)
l = mid;
else
r = mid-1 ;
}
return q[l] == x ? l : -1;
}
int main()
{//贪心,每组人数尽可能多
cin >> n;
for (int i = 0;i < n;i++)
{
cin >> a[i];
}
sort(a, a + n);
for (int i = 0;i < n;i++)
{
//对于队列二分查找,优先插入最后一个
int j = binary_search(a[i] - 1);
if (cnt==0||j == -1)
{
q[cnt] = a[i];
siz[cnt++]++;
}
else
{
q[j] = a[i];
siz[j]++;
}
}
int minl = 100000;
for (int i = 0;i < cnt;i++)
minl = min(minl, siz[i]);
cout << minl;
}
桶方法(这里用map优化)
运用桶方法和铺设道路的题目很像
#include <iostream>
#include <map>
#define MIN 0X3f3f3f
using namespace std;
typedef std::map<int, int>::iterator it;
map<int, int>m;
/*思路:1.运用桶计数(这里优化为map),找出每个元素的高度(堆积木)
2.(进行找人前,必须先把当前的i--,才能确保以下成立)
每次确保右边的高度一定大于当前高度,否则断开
如果大于当前高度,就让他--,表示他加入了我的队伍,相同能力值的人-1
3.如果一个能力值的人全部被领完了,就把他删除了
*/
int main()
{
int n,t;
cin >> n;
int ans = MIN;
for (int i = 0;i < n;i++)
{
cin >> t;
++m[t];
}
while (!m.empty()) //map非空
{
int cnt = 1; //=1是加上了当前人
it i = m.begin(); //i表示当前位置(当前列)的迭代器
it j = m.begin(); //j始终表示下一位置(右边列)的迭代器
//first表示能力值,second表示当前能力值的人数
--(*i).second; //这个人已经组好队了,减一
for (j++;j != m.end()&&(*j).first==(*i).first+1&&(*i).second<(*j).second;i++,j++)
{ //判断j是否到临界点需要在第一个位置,不然后面访问会出错(&&从左到右)
//j.second必须大于i.second,右边那列必须比当前列高,才能保证最小分组
//(*j).first必须大一,表示连续的
//如果符合条件,i和j都指向下一位置
cnt++;
--(*j).second;
}
i = m.begin();
//m.end()表示最后一个元素的下一位置(不存数据)
while (i != m.end() && (*i).second == 0)
{ //抹掉本次组队中,所有人数变为0的位置
m.erase((*i++).first);
}
ans = min(ans, cnt);
}
cout << ans;
}