zoukankan      html  css  js  c++  java
  • bzoj1584 [Usaco2009 Mar]Cleaning Up 打扫卫生 动态规划+思维

    Description

    有N头奶牛,每头那牛都有一个标号Pi,1 <= Pi <= M <= N <= 40000。现在Farmer John要把这些奶牛分成若干段,定义每段的不河蟹度为:

    若这段里有k个不同的数,那不河蟹度为k*k。那总的不河蟹度就是所有段的不河蟹度的总和。

    Input

    第一行:两个整数N,M

    第2..N+1行:N个整数代表每个奶牛的编号

    Output

    一个整数,代表最小不河蟹度

    Sample Input

    13 4
    1
    2
    1
    3
    2
    2
    3
    4
    3
    4
    3
    1
    4

    Sample Output

    11

    题解

    不会做T T看了半天题解

    这题首先可以发现最差情况是将数列a分成n段,代价为n

    那么一定不能有划分出的一段超过√n个

    这样一来每次的转移就是在√n个内了f[i]=min{f[b[j]]+j*j},j<=√n

    其中b[j]表示的是从b[j]+1开始到i,共有j个不同的数字

    对于b数组的维护,可以随意脑补一下

    比如用pre[a[i]]记录a[i]上次出现的位置,c[j]记录b[j]+1到i的不同数字个数

    这样每次i++时,先更新c数组,即如果pre[a[i]]<=b[i],那就无视,不然c[j]++

    如果c[j]++了,那么就要从b[i]+1开始找一个只在b[j]+1到i出现一次的,删到它为止

    复杂度好像应该是n√n了吧

    对于所有,下限至少是n是可以确定的,因为每个单独一段,即可。

    还有这个m是没有什么用的,那么我可以想如果一段的不同个数已经大于√n

    那么其花费已经为n是没有意义的,但是这里的√n,是向下取整的所以还是

    有意义的,可以取到

    所以只需要记录不同的√n的转移即可,那么如何维护,

    是根号n维护对吧,然后也是根号n转移即可,很好想的。

     1 #include<cstring>
     2 #include<cmath>
     3 #include<iostream>
     4 #include<algorithm>
     5 #include<cstdio>
     6 
     7 #define N 207
     8 #define M 40007
     9 using namespace std;
    10 inline int read()
    11 {
    12     int x=0,f=1;char ch=getchar();
    13     while(ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
    14     while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    15     return x*f;
    16 }
    17 
    18 int n,m;
    19 int a[M],f[M];
    20 int b[N],cnt[N];
    21 int pre[M];
    22 
    23 int main()
    24 {
    25     memset(f,127/3,sizeof(f));
    26     memset(pre,-1,sizeof(pre));
    27     n=read();m=read();
    28     int tot=0;
    29     for(int i=1;i<=n;i++)
    30     {
    31         int x=read();
    32         if(x!=a[tot])a[++tot]=x;
    33     }
    34     n=tot;
    35     m=trunc(sqrt(n));
    36     f[0]=0;
    37     for(int i=1;i<=n;i++)
    38     {
    39         for(int j=1;j<=m;j++)
    40             if(pre[a[i]]<=b[j])cnt[j]++;
    41         pre[a[i]]=i;
    42         for(int j=1;j<=m;j++)
    43             if(cnt[j]>j)
    44             {
    45                 int t=b[j]+1;
    46                 while(pre[a[t]]>t)t++;
    47                 b[j]=t;cnt[j]--;
    48             }
    49         for(int j=1;j<=m;j++)
    50             f[i]=min(f[i],f[b[j]]+j*j);
    51     }
    52     printf("%d",f[n]);
    53 }
  • 相关阅读:
    指针的引用
    引用的基础知识
    const的基础用法
    解决'fopen':this function or variable may be unsafe先关问题的方法
    C++类中不写成员函数易犯错模型
    PCB接地设计宝典:ADI资深专家总结的良好接地指导原则
    电路板级的电磁兼容性设计
    自然对流仿真设置
    Pspice 原理图以及仿真图 输出到 word
    信号完整性常见问题
  • 原文地址:https://www.cnblogs.com/fengzhiyuan/p/8184443.html
Copyright © 2011-2022 走看看