3 1 3 3 10 3 4 2 4 4 2 4 3 2 2
2 7
对一段区间的花费是这个区间不同数个数的平方,求整个区间的最小花费。
第一眼看上去是很简单的dp。。但是卡时间。。。
有一种类似链表的做法,在扫一遍的过程中记录每种颜色最后一次出现的位置,链表直接跳到出现下一种没出现过颜色的位置,不用一个一个找。具体做法是加入当前颜色c,若c上次出现的位置是p[c],那么把l[p[c]]和r[p[c]]连接,也就是说跳过p[c]。更新某个点dp的时候,就沿着左链表的路径更新,每走一步都多了一种颜色。
因为前i个数最多花费是i,所以如果不同颜色种数cnt的平方大于i了就不用再继续往下更新了。
#include<iostream> #include<queue> #include<cstring> #include<cstdio> #include<cmath> #include<set> #include<map> #include<vector> #include<stack> #include<algorithm> #define INF 0x3f3f3f3f #define eps 1e-9 #define MAXN 50010 #define MAXM 60 #define MAXNODE 105 #define MOD 100000 #define SIGMA_SIZE 4 typedef long long LL; using namespace std; int N,dp[MAXN],l[MAXN],r[MAXN],p[MAXN],a[MAXN]; map<int,int> mp; int main(){ freopen("in.txt","r",stdin); while(scanf("%d",&N)!=EOF){ mp.clear(); int n=0; memset(l,-1,sizeof(l)); for(int i=0;i<=N;i++) dp[i]=i; for(int i=1;i<=N;i++){ scanf("%d",&a[i]); if(mp[a[i]]==0) mp[a[i]]=++n; else{ int tmp=p[mp[a[i]]]; r[l[tmp]]=r[tmp]; l[r[tmp]]=l[tmp]; } if(i==1||a[i]!=a[i-1]) l[i]=i-1; r[i]=i+1; p[mp[a[i]]]=i; int cnt=0; for(int j=l[i];j!=-1;j=l[j]){ cnt++; dp[i]=min(dp[i],dp[j]+cnt*cnt); if(cnt*cnt>i) break; } } printf("%d\n",dp[N]); } return 0; }