Paint Pearls

Paint Pearls

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5009

dp+双向链表优化

看到题目,很自然地可以定义状态:dp[i]表示涂好a[0...i]的字符串,花费的最小代价.

状态转移方程:dp[i]=min(dp[i],dp[j]+num2),其中num=从a[j]到a[i]不同的数字个数.

时间复杂度为O(n2),对于n=50000的数据,明显会T.

于是,我们需要进行优化。注意到状态数无法化简,考虑优化转移复杂度.

当区间[j+1,i]中包含元素a[j]时,无需再经过这个点,直接跳到a[j-1];

即a[j+1]前面略过a[j],直接为a[j-1],使得现序列中各个不同元素只出现一次.

而这种结构可以用双向链表维护.//之前用的是set,T了后查了下clear()是O(n)的,尴尬...

但是当序列为1,2,3,4,5,6,7这种互不相同的元素时,复杂度仍会退化为O(n2),

这时,则需要用到剪枝的技巧:当num2>i时,肯定不会比一个一个涂色方法更优,

由此,复杂度变为O(n3/2

代码如下:

 1 #include<cstdio>
 2 #include<map>
 3 #define Min(x,y) (x<y?x:y)
 4 #define N 50005
 5 using namespace std;
 6 const int INF=N;
 7 struct List{
 8     int pre,nxt;
 9     List(int _pre=-1,int _nxt=-1){//ÈÃL[0].preÖ¸Ïò-1
10         pre=_pre;
11         nxt=_nxt;
12     }
13 }L[N];
14 int n,a[N],idx,num,dp[N];
15 map<int,int>mp;
16 int main(void){
17     while(~scanf("%d",&n)){
18         mp.clear();
19         for(int i=1;i<=n;++i){
20             scanf("%d",&a[i]);
21             L[i]=List(i-1,i+1);
22         }
23         for(int i=1;i<=n;++i){
24             dp[i]=INF;
25             if(mp.find(a[i])==mp.end()){
26                 mp[a[i]]=i;
27             }else{
28                 idx=mp[a[i]];
29                 L[L[idx].pre].nxt=L[idx].nxt;
30                 L[L[idx].nxt].pre=L[idx].pre;
31                 mp[a[i]]=i;
32             }
33             for(num=1,idx=L[i].pre;idx>=0;idx=L[idx].pre,num++){
34                 dp[i]=Min(dp[i],dp[idx]+num*num);
35                 if(num*num>i)break;
36             }
37         }
38         printf("%d
",dp[n]);
39     }
40 }
原文地址:https://www.cnblogs.com/barrier/p/5998568.html