[DP]洗盘子

题目描述
有N(1<=N<=40000)个奶牛到FJ的餐厅吃饭,餐厅里有M(1<=M<=N)种菜,每头牛有自己喜欢的菜的编号P_i(1<=P_i<=M),每头牛只吃自己喜欢的这道菜。
  牛儿们在外面排着队进来,按照排队顺序一批一批进来,每批可以同时进来任意头牛,每一批吃完(注意包括最后一批)都要进行打扫,如果这批牛中一共需要K种菜,那么吃完后的打扫时间为K*K。
  请你帮助FJ如何安排各批次使得总打扫时间最少。

Input
  第1行:空格隔开的两个整数N和M
  第2到N+1行:第i+1行包含一个整数P_i。

Output
  输出最少打扫时间。

Sample Input
13 4
1
2
1
3
2
2
3
4
3
4
3
1
4

Sample Output
11

Data Constraint

Hint
【样例说明】
前4批每批1头牛,打扫时间各为1,第5批2头牛打扫时间为1,第6批5头牛打扫时间为4,接下来分两批打扫时间各为1,一共11。

分析
我们可以设吃掉第i盘菜的最少时间为fi
首先我们要明确,这个最少时间的总数一定不超过n(一个一个吃都最大为n)
设j为一次吃j盘菜,那么我们显然可知j最大为sqrt(n)(否则就违背上面那个法则了)
那么我们构思一个O(nsqrt(n))的算法
用pi,j表示从第i盘菜往前数,得到j个菜种的最靠近第i盘菜的菜的位置
然后由于每次都需要更新这个值(因为加了第i盘菜嘛)所以我们把pi,j简化为pj
得方程:fi=min{f[pj] -1+j^2
那么我们要怎么维护p?显然如果i是同种菜系那么就不用再进行计算了,所以我们设一个lastk表示第k种编号的菜系最迟出现在什么地方,如果last[dishi]是小于pj的话,显然需要从pj-1继承过来(因为将要多一个菜嘛)
没啦

#include <iostream>
#include <cstdio>
#include <cmath>
#define rep(i,a,b) for (i=a;i<=b;i++)
using namespace std;
int n,m,l;
int dish;
int f[40001],last[40001],p[40001];
int i,j;
int main()
{
    freopen("cleanup.in","r",stdin);
    freopen("cleanup.out","w",stdout);
    scanf("%d%d",&n,&m);
    rep(i,0,m)
    last[i]=-1;
    l=sqrt(n);
    scanf("%d",&dish);
    last[dish]=1;
    p[1]=1;
    f[1]=1;
    rep(i,2,n)
    {
        scanf("%d",&dish);
        p[0]=i;
        for (j=l;j>=1;j--)
        if (last[dish]<p[j]) p[j]=p[j-1];
        f[i]=i;
        rep(j,1,l)
        if (p[j]-1>0)
        f[i]=min(f[i],f[p[j]-1]+j*j);
        last[dish]=i;
    }
    printf("%d",f[n]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值