链接: http://acm.hdu.edu.cn/showproblem.php?pid=5009
题意: 现在你有一个长度为n的空串,你要涂颜色,如果想要把l到r 涂上颜色,那么花费就是l到r的不同颜色数的平方值。
思路: dp dp[i] 表示到位置i 最小花费为多少, 那么他可以从dp[j] (j<i) 转移过来。但是单纯地转移肯定是会TLE的,所以可以用双向链表来维护 不同数的个数,我设一个nxt 和pre 那么pre就指向位置i前一个和他位置不相同,并且我要保证每个颜色只出现一次。对于样例2 如果我扫到了地第三个4 那么pre[5]=3; nxt[3]=5; pre[3]=1; nxt[1]=3;
对颜色离散化一下。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N =5e4+5;
ll dp[N];
int a[N];
int tmp[N];
int n;
int col[N];
int nxt[N];
int pre[N];
int vis[N];
int main()
{
while(scanf("%d",&n)!=EOF)
{
int cc=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
col[++cc]=a[i];
}
int xx=0;
for(int i=1;i<=n;i++){
if(a[i]!=a[i-1]){
tmp[++xx]=a[i];
}
}
n=xx;
for(int i=1;i<=n;i++) a[i]=tmp[i];
sort(col+1,col+cc+1);
cc=unique(col+1,col+cc+1)-(col+1);
for(int i=1;i<=n;i++){
int id=lower_bound(col+1,col+cc+1,a[i])-(col);
a[i]=id;
}
for(int i=0;i<=n;i++) dp[i]=i;
for(int i=1;i<=n;i++){
pre[i]=i-1;
nxt[i]=i+1;
}
pre[0]=-1;
memset(vis,-1,sizeof(vis));
for(int i=1;i<=n;i++){
if(vis[a[i]]==-1) vis[a[i]]=i;
else{
int id=vis[a[i]];
nxt[pre[id]]=nxt[id];
pre[nxt[id]]=pre[id];
vis[a[i]]=i;
}
ll kk=0;
for(int j=pre[i];j!=-1;j=pre[j]){
kk++;
if(kk*kk>i) break;
dp[i]=min(dp[i],dp[j]+kk*kk);
}
}
printf("%lld\n",dp[n]);
}
return 0;
}