1584: [Usaco2009 Mar]Cleaning Up 打扫卫生

来源:互联网 发布:临淄广电网络客服电话 编辑:程序博客网 时间:2024/03/29 02:21

题目链接

题目大意:n个数,分成若干段,若该段中有k个不同的数,定义该段的费用k*k,求最小费用

题解:只会n^2……f(i)=min{f(j)+sum(j+1,i)2}(0j<i)
观察到对于一个区间,其答案上界为len,也就是说区间内最多len种不同的数


pos[j]ij1(便)

f(i)=min{f(pos(j))+jj}(0jn)

pos(j)O(nn)

记录pre[i]为i位置的数上次出现的位置,cnt[j]为[pos[j]+1,i]中不同数字个数

跪啊

我的收获:寻找答案上界奇妙优化,数学大法

#include <iostream>#include <cstring>#include <cstdio>#include <cmath>#include <algorithm>using namespace std;#define M 50005int n,m,a[M];int pos[M],cnt[M],f[M];int pre[M],nxt[M],last[M];void Dp(){    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];//a[i]上次位置不在[pos[j]+1,i]中,加入a[i]         for(int j=1;j*j<=n;j++)            if(cnt[j]>j)//超出范围             {                pos[j]++;//移动左端点pos[j]+1,暴力移动pos(j),直到完全删掉1个数字,意会一下                while(nxt[pos[j]]<=i) pos[j]++;//均摊O(1)                 cnt[j]--;            }        for(int j=1;j*j<=n;j++) f[i]=min(f[i],f[pos[j]]+j*j);//转移     }}void work(){    Dp();    printf("%d\n",f[n]);}void init(){    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++) scanf("%d",&a[i]),pre[i]=last[a[i]],last[a[i]]=i;    memset(last,0,sizeof(last));    for(int i=n;i>=1;i--) nxt[i]=last[a[i]],last[a[i]]=i;    for(int i=1;i<=n;i++) if(!nxt[i]) nxt[i]=n+1;//net数组注意赋值!! }int main(){    init();    work();    return 0;}

正常做法

实现的时候直接用双向链表删掉相同的位置,只保留最后一个

#include <iostream>#include <cstring>#include <cstdio>#include <cmath>#include <algorithm>using namespace std;#define M 50005int n,a[M];int pos[M],f[M];int pre[M],nxt[M];void del(int x){    nxt[pre[x]]=nxt[x];    pre[nxt[x]]=pre[x];}void Dp(){    memset(f,0x3f,sizeof(f));f[0]=0;    for(int i=1;i<=n;i++)    {        if(!pos[a[i]]) pos[a[i]]=i;        else del(pos[a[i]]),pos[a[i]]=i;        int cnt=0;        for(int j=pre[i];j!=-1;j=pre[j]){            cnt++;            f[i]=min(f[i],f[j]+cnt*cnt);            if(cnt*cnt>i) break;        }    }}void work(){    Dp();    printf("%d\n",f[n]);}void init(){    scanf("%d%*d",&n);    for(int i=1;i<=n;i++) scanf("%d",&a[i]);    for(int i=1;i<=n;i++) pre[i]=i-1,nxt[i]=i+1;    pre[0]=-1;}int main(){    init();    work();    return 0;}
原创粉丝点击