Paint Pearls

时间:2023-03-09 04:39:31
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

代码如下:

 #include<cstdio>
#include<map>
#define Min(x,y) (x<y?x:y)
#define N 50005
using namespace std;
const int INF=N;
struct List{
int pre,nxt;
List(int _pre=-,int _nxt=-){//ÈÃL[0].preÖ¸Ïò-1
pre=_pre;
nxt=_nxt;
}
}L[N];
int n,a[N],idx,num,dp[N];
map<int,int>mp;
int main(void){
while(~scanf("%d",&n)){
mp.clear();
for(int i=;i<=n;++i){
scanf("%d",&a[i]);
L[i]=List(i-,i+);
}
for(int i=;i<=n;++i){
dp[i]=INF;
if(mp.find(a[i])==mp.end()){
mp[a[i]]=i;
}else{
idx=mp[a[i]];
L[L[idx].pre].nxt=L[idx].nxt;
L[L[idx].nxt].pre=L[idx].pre;
mp[a[i]]=i;
}
for(num=,idx=L[i].pre;idx>=;idx=L[idx].pre,num++){
dp[i]=Min(dp[i],dp[idx]+num*num);
if(num*num>i)break;
}
}
printf("%d\n",dp[n]);
}
}