题意:有一个序列,你需要给它上颜色。每次只能上一段连续的区间,费用是这个区间里颜色数的平方,问全部上色完成的最小花费是多少。
思路:dp。首先是缩点,把相邻同色的合并起来,然后把颜色值离散化。完了就可以dp了。dp(i)是从头上色到i时的最小花费。dp(i)可以从dp(0)~dp(i-1)转移过来,还有就是需要极致地优化。。如果这一段的花费比到最后还高,就可以跳出了。
#include<iostream>
#include<cmath>
#include<queue>
#include<map>
#include<vector>
#include<algorithm>
#include<string.h>
#include<cstdio>
using namespace std;
struct node{
int val;
int idx;
int rnk;
};
node a[50010];
bool vis[50010];
int dp[50010];
int vec[50010]; //数组优化,避免每次清空全部vis数组
bool cmp1(node n1,node n2){
return n1.val<n2.val;
}
bool cmp2(node n1,node n2){
return n1.idx<n2.idx;
}
int main(){
int n;
a[0].val=-1;
while(~scanf("%d",&n)){
int m=0;
for(int i=1;i<=n;i++){
int t;
scanf("%d",&t);
if(a[m].val!=t)m++;
a[m].idx=m;
a[m].val=t;
}
sort(a+1,a+m+1,cmp1);
int k=0;
for(int i=1;i<=m;i++){
if(a[i].val!=a[i-1].val)k++;
a[i].rnk=k;
}
sort(a+1,a+m+1,cmp2);
for(int i=0;i<=m;i++)dp[i]=i;
for(int i=0;i<m;i++){
int cnt=0;
int siz=0;
for(int j=i+1;j<=m;j++){
if(!vis[a[j].rnk]){
vec[siz++]=a[j].rnk;
vis[a[j].rnk]=1;
cnt++;
}
if(dp[i]+cnt*cnt>=dp[m])break; //不可能取得最优解及时跳出
dp[j]=min(dp[j],dp[i]+cnt*cnt);
}
for(int j=0;j<siz;j++){
vis[vec[j]]=0;
}
}
printf("%d\n",dp[m]);
}
return 0;
}