[USACO09MAR]Cleaning Up

本文介绍了一种优化的动态规划算法,用于解决给定序列的最优划分问题,目标是最小化各子序列不同元素数量的平方和。通过限制不同元素数量不超过平方根(n),并维护每个元素最近出现的位置,实现了高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:
  给你一个长度为n的序列a,你可以将其分为若干段,最终的答案为每一段不同数个数的平方和。

思路:
  不难想到一个O(n^2)的DP:
    f[i]=min{f[j]+cnt(j,i)^2}
  考虑一些优化。
  首先不难发现,答案最坏不会超过n。(一个数一段)
  要让答案更优,一段内不同数的个数不会超过sqrt(n)。(不然平方之后就超过n了)。
  我们把到i有j个不同数的最后位置记作pos[j]。
  考虑如何维护这个pos[j]。
  我们可以先将每个数字出现的上一个位置记作last[i],一段中出现的不同数字个数记作cnt[i]。
  首先,如果last[a[i]]<=pos[j],则cnt[j]++。
  当cnt[j]>j时,把左端点往右缩,如果这时last[a[pos[j]]]==pos[j],cnt[j]--。

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<cctype>
 4 #include<algorithm>
 5 inline int getint() {
 6     register char ch;
 7     while(!isdigit(ch=getchar()));
 8     register int x=ch^'0';
 9     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
10     return x;
11 }
12 const int inf=0x7fffffff;
13 const int N=40001,M=40001;
14 int a[N],f[N],pos[M],last[M],cnt[M];
15 int main() {
16     int n=getint(),m=getint();
17     for(register int i=1;i<=n;i++) {
18         const int x=getint();
19         if(x!=a[a[0]]) a[++a[0]]=x;
20     }
21     n=a[0],m=sqrt(a[0]);
22     for(register int i=1;i<=n;i++) {
23         f[i]=inf;
24         for(register int j=1;j<=m;j++) {
25             if(last[a[i]]<=pos[j]) cnt[j]++;
26         }
27         last[a[i]]=i;
28         for(register int j=1;j<=m;j++) {
29             while(cnt[j]>j) {
30                 pos[j]++;
31                 if(last[a[pos[j]]]==pos[j]) cnt[j]--;
32             }
33         }
34         for(register int j=1;j<=m;j++) {
35             f[i]=std::min(f[i],f[pos[j]]+j*j);
36         }
37     }
38     printf("%d\n",f[n]);
39     return 0;
40 }

 

转载于:https://www.cnblogs.com/skylee03/p/8185129.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值