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\n",dp[n]); 39 } 40 }