题解 P2062 【分队问题】
这道题可以用DP来做。
我们定义状态$f[i]$为是当前的人数为$i$时的队伍数量的最大值。而$i$同时也代表现在是第几个人。
根据题意。我们可以推出转移方程
-
当$i<a[i]$时: $f[i]=f[i-1]$
- 当前队伍中的人数$i$不大于$a[i]$时,我们显然不能让第$i$个选手自己组成一个队伍,只能让第$i$个选手加入之前的队伍。
-
当$i>=a[i]$时: $f[i]=max(f[i-1],f[i-a[i]]+1)$
- 当前队伍中的人数$i$比$a[i]$大时,就出现了两种情况。第一种仍是让第$i$个选手加入之前的队伍,第二种则是让第$i$个选手自成一个队伍,而这样的话自然得从$f[i-a[i]]$中转移而来,因为第$i$个选手自成一个队伍的条件是必须满足有至少$a[i]$个人。
上面两个转移方程显然都是从$i$之前转移而来的,所以循环顺序是顺序。
而且因为循环中$i$是递增的,所以要先排序$a$数组。
此时有些人可能觉得: 排序了之后$i$和$a[i]$就不是对应的了。
当然跟原来的数据来比不是对应的了,但是:这个做法跟$i$的顺序完全没有关系 ,只要保证排完之后还有这$i$个$a[i]$就行了。这个性质也方便了上面的转移方程 (上面的做法直接默认排完之后的$a[i]$和$i$是对应的了)
AC代码
#include <cstdio>
#include <algorithm>
#define max(a,b) (a)>(b)?(a):(b)
const int N = 1e6+10;
int n;
int a[N];
int f[N];
void Input() {
scanf("%d",&n);
for(int i=1; i<=n; i++) {
scanf("%d",&a[i]);
}
}
void Solve() {
std::sort(a+1,a+n+1);
for(int i=1; i<=n; i++) {
if(i >= a[i]) {
f[i]=max(f[i-1],f[i-a[i]]+1);
} else {
f[i]=f[i-1];
}
}
printf("%d",f[n]);
}
int main(void) {
Input();
Solve();
return 0;
}
写得非常详细了,望管理大大给过。