P4447 [AHOI2018初中组]分组

题目分析:要选出最小分组的最大值,而不是要选出最小的一个分组,那样的话答案必然只会是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;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值