【DP,贪心】Day 5 提高组模拟C组 T1 分队问题

题目链接

https://www.luogu.org/problemnew/show/P2062

题目大意

给定一个长度为 n 的序列,将其分为若干个序列,每个序列的长度必须大于该序列中的最大值,问最多可以划分成几个序列

解题思路

首先对该序列进行排序,然后进行动态转移
f [ i ] 表示前 i 个数可以组成的最多序列的长度,我们可以发现 f [ i ] 只和 f [ i a [ i ] ] 有关系,那么我们可以得到状态转移方程

f [ i ] = m a x { f [ k ] } + 1 ( 0 <= k < a [ i ] )

但是由于数据太大,这样子是会超时的,所以我们需要用一个数组 g 来维护 f [ 1.. k ] 之间的最大值,所以可以得到新的方程
f [ i ] = f [ i g [ i ] ] + 1

g [ i ] = m a x { f [ i ] , g [ i 1 ] }

DP代码

#include<cstdio>
#include<algorithm>
using namespace std;
int a[1000001],n,now,ans,maxs,f[1000001],g[1000001];
int main()
{
    scanf("%d",&n); 
    for(int i=1;i<=n;i++) scanf("%d",a+i);
    sort(a+1,a+1+n);//排序
    for(int i=1;i<=n;i++)
    {
        if(i>=a[i]) f[i]=g[i-a[i]]+1;//动态转移
        g[i]=max(f[i],g[i-1]);//保存最大的f[k]
    }
    printf("%d",f[n]);
}

然后我们学校的一位大佬用了贪心过了,我也不知道原理,就在这里放一下代码吧

贪心代码

#include<vector>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[1000001],n,now,ans,maxs;
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);
    now=a[1];maxs=a[1];
    for(int i=1;i<=n;i++)
    {
        if(a[i]<now)
        now=a[i],maxs=a[i];
        --now;
        if(!now)
        now=a[i],maxs=a[i],ans++;
    }
    printf("%d",ans);
}

时间复杂度

由于上述方法都是拍了序后循环了一遍 n ,所以时间复杂度都是 O ( n l o g n + n )

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/80985320