zoukankan      html  css  js  c++  java
  • 【BZOJ】1584: [Usaco2009 Mar]Cleaning Up 打扫卫生

    【算法】DP+数学优化

    【题意】把n个1~m的数字分成k段,每段的价值为段内不同数字个数的平方,求最小总价值。n,m,ai<=40000

    【题解】

    参考自:WerKeyTom_FTD

    令f[i]表示把前i个数分成若干段的最小价值。

    转移中我们定义,从i开始往前到有j个不同的数的最小位置为b[j]。

    f[i]=f[b[j]-1]+j^2。

    考虑最坏情况,每个数自成一段,则总价值为n。

    所以当段内不同的数个数>√n时,就不可能是最优解了(此时价值>n)。

    所以f[i]=f[b[j]-1]+j^2,1<=j<=√n。

    快速计算的关键在递推b[j]数组,首先在递推过程中同步计算上一个等数位置last[]和桶c[]。

    枚举j:

    如果last[i]>=b[j],不会新增数,不改变。

    否则,b[j]++直到c[a[b[j]]]=b[j],此时b[j]++得到新的b[j]。

    注意若元素个数不满√n个,出现新元素就top++。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int maxn=40010;
    
    int n,N,a[maxn],last[maxn],c[maxn],b[maxn],f[maxn],m,top;
    int main(){
        scanf("%d%d",&n,&N);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        m=(int)sqrt(n)+1;
        top=0;
        for(int i=1;i<=n;i++){
            last[i]=c[a[i]];
            c[a[i]]=i;
            for(int j=1;j<=min(m,top);j++)if(last[i]<b[j]){
                while(c[a[b[j]]]!=b[j])b[j]++;
                b[j]++;
            }
            if(!last[i])b[++top]=1;
            f[i]=i;
            for(int j=1;j<=min(m,top);j++)f[i]=min(f[i],f[b[j]-1]+j*j);
        }
        printf("%d",f[n]);
        return 0;
    }
    View Code
  • 相关阅读:
    Windows常用内容渗透命令
    字符串编码小记
    卡巴斯基命令解析
    聚合与继承
    Maven与Eclipse整合
    maven系列三:Maven核心概念
    使用Maven构建项目
    Maven项目构建过程练习
    maven系列二:Maven入门
    使用Maven构建多模块项目
  • 原文地址:https://www.cnblogs.com/onioncyc/p/7576872.html
Copyright © 2011-2022 走看看