P2062 分队问题(贪心orDP)
题目描述
给定n个选手,将他们分成若干只队伍。其中第i个选手要求自己所属的队伍的人数大等于a[i]人。
在满足所有选手的要求的前提下,最大化队伍的总数。
注:每个选手属于且仅属于一支队伍。
输入输出格式
输入格式:
第一行一个整数n,表示人数。
以下n行,每行一个整数表示a[i]。
输出格式:
输出队伍总数的最大值。数据保证有解。
输入输出样例
输入样例#1: 复制
5
2
1
2
2
3
输出样例#1: 复制
View Code
View Code
2
分析:(贪心)
贪心的思想就是尽量将要求大的人放在一起。所以我们要先排个序,从大到小,然后用一个now表示当前队列还需多少个人可以满足条件 初始代码:
now=a[1];
for (int i=1;i<=n;i++)
{
now--;
if (!now)
{
now=a[i];
s++;
w=false;
}
}
然后我们可以发现:
8
1 2 4 5 5 5 5 5
这一组数据中我们会将4归入第二个队列,导致第二个队列不成立,所以我们需要进行优化该情况。如果我们发现将目前在队列里的的归入前一个队列会更优的话我们就可以直接改变now
#include<cstdio> #include<algorithm> using namespace std; int n,a[1000001],now,s,maxs; bool w; bool cmp(int x,int y) { return x>y; } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+1+n,cmp); w=true;//表示是否是第一个队列,如果是就不可以归入前一个队列了 now=a[1];//从第一个开始 for (int i=1;i<=n;i++) { if (a[i]<now&&!w)//比较 { now=a[i]; } now--; if (!now)//一个队列已经满足要求 { now=a[i]; s++; w=false; } } printf("%d",s); }
(DP)
设 f[i]f[i] 表示前i个人能够分成的最大的队伍个数
从小到大排序一遍之后,显然可以发现,当第i个人可以加入这个队伍时,当且仅当 i>=a[i]i>=a[i]
所以可以得到一个转移方程: f[i]=max(f[k])+1(0<i<=a[i])f[i]=max(f[k])+1(0<i<=a[i])
但是这样对于百万级别的n是会超时的
考虑怎么去优化它
我们可以开一个数组来储存 f[1...n]f[1...n] 的最大值
则转移方程可以改成: f[i]=g[i-a[i]]+1f[i]=g[i−a[i]]+1
对于g数组的维护: g[i]=max(g[i-1],f[i])g[i]=max(g[i−1],f[i])
这样就可以完成O(n)转移
#include<bits/stdc++.h> using namespace std; int a[1000045]; int f[1000045]; int s[1000045]; int main( ) { int n; scanf("%d",&n); for(int i=1 ; i<=n ; i++) scanf("%d",&a[i]); sort(a+1,a+n+1); for(int i=1 ; i<=n ; i ++) { if(i>=a[i]) f[i]=s[i-a[i]]+1; else f[i]=0; s[i]=max(f[i],s[i-1]); } printf("%d\n",f[n]); }