洛谷P2943 清理——DP

题目:https://www.luogu.org/problemnew/show/P2943

一眼看去就有个 n^2 的做法:f[i] = min{ f[j] + num( i - j ) * num( i - j ) } , 1 <= j < i;

但仔细想想这个做法,发现那个num数组很不好处理;

也就是我们需要关注一个区间内食品的种类数;

不妨根据这个来调整做法,直接枚举种类数;

这样做的好处是可以进行转移,因为从 i 到 i+1 会带来一个种类数量的改变;

由此受到启发,设数组 pos[j] 为种类数是 j 时可到达的最远节点(越远越优);

再对每个点记录一下前驱后继以便于进行种类数是否增加的判断;

cnt数组记录 pos[j] 到 i 这一段(实际的)种类数,就可以转移了。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int const maxn=40005;
int n,m,f[maxn],pos[maxn],cnt[maxn],pre[maxn],nxt[maxn],lst[maxn],a[maxn];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1,x;i<=n;i++)
    {
        scanf("%d",&x);a[i]=x;
        pre[i]=lst[x];
        nxt[lst[x]]=i;
        lst[x]=i;
        nxt[i]=n+1;
    }
    for(int i=1;i*i<=n;i++)pos[i]=1;//!
    memset(f,0x3f,sizeof f);
    f[0]=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j*j<=n;j++)
        {
            if(pre[i]<pos[j])cnt[j]++;
            if(cnt[j]>j)
            {
                cnt[j]--;
                while(nxt[pos[j]]<=i)pos[j]++;
                pos[j]++;
            }
            f[i]=min(f[i],f[pos[j]-1]+j*j);
        }
    printf("%d",f[n]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Zinn/p/9164456.html