HDU 5009 DP (2014 ACM/ICPC Asia Regional Xi'an Online)题解

来源:互联网 发布:网络测试仪 编辑:程序博客网 时间:2024/05/21 06:42

题意:

给N个数,a1,a2....an,表示目标状态,初始状态为空,每次操作可以将一段连续的区间变成目标状态,花费为这段区间出现的不同的数字数的平方。求最后变到目标状态的最小花费

题解:

dp[i]表示涂完前i个珠子需要的代价。显然,dp[i] <= i。根据题目中的规则,可以推导出:

    dp[i] = min( dp[m-1] + k(m, i)^2 )。k(m, i)表示第m个珠子到第i个珠子中不同颜色的个数。因为dp[n] <= n <= 50000,那么当k(m, i)^2 >= 50000时就不用转移了,也就是k(m, i) <= n ^ 0.5。这样复杂度降为O(n * n ^ 1/2),可以接受。
    接下来就是要快速的求得k(m, i)的值。方法也有很多,这里用cnt来计数,用数组vis[]做标记。
代码如下:
#include<stdio.h>#include<algorithm>#include<vector>#include<string.h>#define LL long long int#define MAX 100009using namespace std;LL n,m,k;LL num[MAX],t[MAX];LL dp[MAX];//dp[i]表示到i位置的最优值bool vis[MAX];void init(){    sort(t+1,t+1+n);    t[0]=0;//一定要加上,以便后面用Lowerbound();    for(int i=2;i<=n;i++){        if(t[i]!=t[i-1]){            t[++m]=t[i];           // printf("%I64d ",t[m]);        }        if(num[i]!=num[i-1]){            num[++k]=num[i];        }    }    for(int i=1;i<=n;i++){        int pos=lower_bound(t,t+m+1,num[i])-t;        num[i]=pos;//把10^9的数压缩到10^5之内    }}void deal(){    vector<int> vec;    vec.clear();    memset(vis,false,sizeof(vis));    for(int i=1;i<=n;i++) dp[i]=1<<30;    dp[0]=0;    dp[n]=k;    for(int i=0;i<=n;i++){        int cnt=0;        for(int j=i+1;j<=n;j++){            if(vis[num[j]]==false){                vec.push_back(num[j]);                cnt++;//从i到j之间,有多少种颜色            }            vis[num[j]]=true;            if(dp[i]+cnt*cnt>dp[n]) break;//因为总的点为k(除去了,连续的相同的颜色以后),那么最大不能超过dp[n]            dp[j]=min(dp[j],dp[i]+cnt*cnt);        }        int siz=vec.size();        for(int i=0;i<siz;i++){            vis[vec[i]]=false;        }        vec.clear();    }}int main(){    while(~scanf("%I64d",&n)){        k=m=1;        for(int i=1;i<=n;i++){            scanf("%I64d",&num[i]);            t[i]=num[i];        }        init();        deal();        printf("%I64d\n",dp[n]);    }return 0;}


0 0