题目描述
给定n 个选手,将他们分成若干只队伍。其中第i 个选手要求自己所属的队伍的人数大等于a[i]人。
在满足所有选手的要求的前提下,最大化队伍的总数。注:每个选手属于且仅属于一支队伍。
输入格式
第一行一个整数n,表示人数。
以下n 行,每行一个整数表示a[i]。
输出格式
输出队伍总数的最大值。数据保证有解。
样例输入
5
2
1
2
2
3
样例输出
2
数据范围
对于20%的数据,n <= 10
对于40%的数据,n <= 1000
对于60%的数据,n <= 10000
对于100%的数据,1 <= n <= 10^6
Solution:
最开始想到贪心。
先把A[ i ] 排序。
然后我们从大到小依次加入队伍就好了。
3 2 2 2 1
先把 3 2 2 加入
再把 2 1 加入 就可以了。
但是实际上这东西是错的。
例子
3 3 3 3 2 2 1
变成 3 3 3。3 2 1。
实际上可以 1 。2 2。3 3 3 3
所以贪心有问题。
考虑DP
F[i] 表示前 i 个人可以最多分成的队伍数目。
显然 如果第 i 个人对队伍数目做出贡献,当且仅当 满足了 第i个人的要求 A[i]
所以 F [ i ] =Max{F[ k ] } +1 其中 0 < K < = i - A[ i ]
所以 可以维护 一个 最大值 G [ k ] 表示 F[ 1..k ] 的 最大值。
每次修改 了 F [ i ] 的话 就是 G [ i ] =Max { F [ i ], G [ i -1 ] }
这就避免了每次查找 max F [ k ] 都要从 头 到 i- A [ i ] 扫描一遍。
所以时间复杂度 为 O(N)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=1e6+5,inf=x3f3f3f3f;
inline void _read(int &x){
char t=getchar();bool sign=true;
while(t<'0'||t>'9')
{if(t=='-')sign=false;t=getchar();}
for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
if(!sign)x=-x;
}
int n,f[maxn],s[maxn],g[maxn];
int main(){
_read(n);
int i,j,cur;
for(i=1;i<=n;i++)_read(s[i]);
sort(s+1,s+1+n);
for(i=1;i<=n;i++){
if(i<s[i]){g[i]=g[i-1];continue;}
cur=i-s[i];
if(cur>i-1)cur=i-1;
f[i]=g[cur]+1;
g[i]=max(g[i-1],f[i]);
}
cout<<f[n];
}