zoukankan      html  css  js  c++  java
  • hdu-5009 Paint Pearls DP+双向链表 with Map实现去重优化

    http://acm.hdu.edu.cn/showproblem.php?pid=5009

    题目要求对空序列染成目标颜色序列,对一段序列染色的成本是不同颜色数的平方。

    这题我们显然会首先想到用DP去解决,dp[i] = min( dp[i] , dp[j] + cost(i , j) )。但是枚举ij的复杂的高达n^2,不能接受,我们就考虑去优化它。

    首先比较容易想到的是,去除连续重复元素,可以把它们当作同一个点处理。

    此外在遍历j的过程中如果当前序列颜色不同的数量平方大于当前dp[i],显然已经没有一并涂色以及继续扩充序列的必要了(随着序列数的增长,不同颜色的数量是单调递增的,必然在之后不会出现小于dp[i]的情况。

    但是这样并不能把复杂的降低至sqrt(n)*n,因为我们枚举的子序列会有重复元素,所以并不是每次j的变化都会来带不同颜色数的增长,比如24242424这种。那么我们考虑这种情况该如何优化,我们先换一个比较容易理解的例子9871341,当我们的i指向最后一个1的时候,我们的j开始向前遍历,当遍历到上一个i的时候,其实我们已经知道,这个i我们把它包含进去是没有成本的(因为之前已经有1了,并不会使不同颜色的数增加)所以我们应该不假思索地加入这个数。但是计算机是很蠢的,它每次依旧会遍历到它,别看这遍历一个单位很快,像24242424这种就会导致计算机大量重复地枚举了。为此我们模拟一个双向链表,在枚举i地过程中,我们维护前缀序列地链表全都是不同的元素,这个实现起来其实并不难,因为i每次增长最多会增加一个重复元素,相应的我们也只需要去除一个重复元素(即上一次出现当前值的那个位置)。然后在遍历j的时候只需要遍历链表就好了。这样我们保证j遍历过程中每次都能增加一个不同颜色,复杂的自然降低到n*sqrt(n)了。

    #include <iostream>
    #include <algorithm>
    #include <map>
    #include <set>
    #include <vector>
    #include <cstdio>
    #define LL int
    using namespace std;
    const int N=50005;
    const LL inf=1e8+1;
    LL arr[N];
    LL dp[N];
    int pre[N];
    int nex[N];
    int main()
    {
        cin.sync_with_stdio(false);
        int n;
        while(cin>>n)
        {
            int p=0;
            for(int i=0; i<n; i++)
            {
                int c;
                //c=_read();
                cin>>c;
                if(i==0||arr[p]==c)
                    arr[p]=c;
                else
                    arr[++p]=c;
            }
            map<LL,int> unq;
            int rk=0;
            for(int i=0; i<=p; i++)
            {
                map<LL,int>::iterator it=unq.find(arr[i]);
                if(it!=unq.end())
                    arr[i]=it->second;
                else
                {
                    unq[arr[i]]=rk;
                    arr[i]=rk;
                    rk++;
                }
                pre[i]=i-1;
                nex[i]=i+1;
            }
            dp[p+1]=0;
            map<LL,int> v;
            for(int i=p; i>=0; i--)
            {
                LL ans=inf;
                if(v.find(arr[i])==v.end())
                    v[arr[i]]=i;
                else
                {
                    int ix=v[arr[i]];
                    nex[pre[ix]]=nex[ix];
                    pre[nex[ix]]=pre[ix];
                    v[arr[i]]=i;
                }
                int rx;
                int cnt=0;
                for(int j=i; j!=p+1; j=nex[j])
                {
                    //cout<<j<<endl;
                    cnt++;
                    LL p2=cnt*cnt;
                    if(p2>ans)
                        break;
                    if(p2+dp[nex[j]]<ans)
                    {
                        ans=p2+dp[nex[j]];
                        rx=j;
                    }
                    //ans=min(ans,(LL)(xx+dp[j+1]));
                    //cout<<ans<<' '<<s.size()<<' '<<dp[j+1]<<endl;
                }
                dp[i]=ans;
                //cout<<dp[i]<<' '<<i<<' '<<rx<<endl;
            }
            //printf("%d
    ",dp[0]);
            cout<<dp[0]<<endl;
        }
        return 0;
    }
  • 相关阅读:
    OSI安全体系结构
    PHP 二维数组根据相同的值进行合并
    Java实现 LeetCode 17 电话号码的字母组合
    Java实现 LeetCode 16 最接近的三数之和
    Java实现 LeetCode 16 最接近的三数之和
    Java实现 LeetCode 16 最接近的三数之和
    Java实现 LeetCode 15 三数之和
    Java实现 LeetCode 15 三数之和
    Java实现 LeetCode 15 三数之和
    Java实现 LeetCode 14 最长公共前缀
  • 原文地址:https://www.cnblogs.com/LukeStepByStep/p/8604648.html
Copyright © 2011-2022 走看看