hdu 5009 Paint Pearls(dp)

题意:有n个珠子,每个珠子有一个想要被涂成的颜色,每次可以选择一个区间进行涂色,花费为区间中不同颜色的珠子的数量的平方,求最小的花费将所有的珠子涂成目标颜色。

思路:可以假定涂色是从左到右一段一段涂的,不会选择相交的区间(不会得到任何好处)。那么用dp[i]表示涂完1~i的最小花费,dp[i] = min(dp[j] + differentcolor[j+1][i]^2),其中j < i。但是这样写复杂度很高,发现要增加的花费,即differentcolor[j+1][i] <= totalcolor^2,即最差也可以将n个珠子直接合并。那么其实这个状态只要向前找sqrt(n)个就行了。从左到右扫一遍,维护从位置j到当前位置i有k个不同的颜色的最小dp值的位置minp[i]。若lastp[c]表示上一个颜色c出现的位置,那么对于当前位置i,lastp[color[i]] + 1 ~ i的所有位置的不同颜色数都要加1,扫一遍minp,我们就知道哪些minp[c]要变成minp[c+1]了。吐槽一下数据,真是太水啦~


代码:


#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn = 50000 + 10;
ll dp[maxn];
int color[maxn],minp[255];
int lastp[maxn];
map<int,int>mp;
int main()
{
//    freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;
    while(~scanf("%d",&n))
    {
        mp.clear();
        int cnt = 0;
        for(int i = 1;i <= n;++i)
        {
            scanf("%d",&color[i]);
            if(mp.find(color[i]) == mp.end())
            {
                mp[color[i]] = ++cnt;
                color[i] = cnt;
            }
            else
                color[i] = mp[color[i]];
        }
        int m = min(cnt,(int)sqrt(n) + 1);
        memset(lastp,0,sizeof(lastp));
        memset(minp,0,sizeof(minp));
        dp[0] = 0;
        for(int i = 1;i <= n;++i)
        {
            dp[i] = dp[i-1] + 1;
            for(int j = m;j >= 1;--j)
            {
                if(minp[j] > lastp[color[i]])
                    minp[j] = 0;
                if(minp[j-1] && minp[j-1] > lastp[color[i]])
                {
                    if(minp[j] == 0)
                        minp[j] = minp[j-1];
                    else if(dp[minp[j]-1] > dp[minp[j-1]-1])
                        minp[j] = minp[j-1];
                }
                if(minp[j])
                    dp[i] = min(dp[i],dp[minp[j]-1] + (ll)j*j);
            }
            if(!minp[1])
                minp[1] = i;
            else if(dp[i-1] <= dp[minp[1]-1])
                minp[1] = i;
            lastp[color[i]] = i;
        }
        printf("%I64d\n",dp[n]);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值