题解 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;
}

写得非常详细了,望管理大大给过。

posted @ 2019-02-07 22:03  加固文明幻景  阅读(7)  评论(1编辑  收藏  举报  来源