HDU 4261 Estimation (DP+优先队列优化)

来源:互联网 发布:单片机开发步骤 编辑:程序博客网 时间:2024/05/22 14:26

转载请注明出处,谢谢http://blog.csdn.net/acm_cxlove/article/details/7854526       by---cxlove

题目:给出一个序列A,分为K个部分,然后每个部分给出一个B,使得所有的sigma(|Ai-Bi|)最小

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

对于某一段,肯定是将B取为他们的中位数是最优的,开始以为是平均数,弱爆了。

容易想到DP可做,dp[i][j]表示前i个数,分为j个部分的最优解。

dp[i][j]=min(dp[r][j-1]+sum(r+1,j))   其中sum(r+1,j)表示 从r+1到j分为一个部分的最小差值

DP是n^2*k,但是sum部分的复杂度还比较高。必须先预处理

用双堆优化,写成双优先队列优化。

维护两个堆,一个存比较小的数,一个存比较大的数,新加的数容易判断和当前的中位数的大小,加入其中的堆

然后维护一下两个堆的数量均衡,保证中位数的位置确定,我取的是大堆中的最小值。

结果就是大堆和与小堆和的差,注意奇数个的情况。

另外这题卡时比较紧,注意姿势

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#define inf 1<<29using namespace std;int n,k,a[2005];int b[2005][2005];   //存放i到j的最小差值int dp[2005][26]={0};   //dp[i][j]表示前i个数,分为j段的最优解//以下两个优先队列,记录中位数//如果为奇数个元素,中位数便是大的里面的最小值//如果为偶数则为小的最大值与大的最小值之间的任意数priority_queue<int>lower;priority_queue<int,vector<int>,greater<int> >upper;int main(){    while(scanf("%d%d",&n,&k)!=EOF&&n+k){        for(int i=1;i<=n;i++) scanf("%d",&a[i]);        for(int i=1;i<=n;i++) for(int j=0;j<=k;j++) dp[i][j]=inf;        for(int i=1;i<=n;i++){            while(!lower.empty()) lower.pop();            while(!upper.empty()) upper.pop();            int sum=0; //sum表示大堆和与小堆和的差            for(int j=i;j<=n;j++){                //判断是加入小的,还是大的                if(lower.empty()||a[j]<=lower.top()){                    lower.push(a[j]);                    sum-=a[j];                }                else{                    upper.push(a[j]);                    sum+=a[j];                }                //计算小堆里面的数量,大堆里面的数量                int low=(j-i+1)/2,high=(j-i+1)-low;                //作一次调整,使得数量保持一致                if(lower.size()>low){                    upper.push(lower.top());                    sum+=lower.top()*2;                    lower.pop();                }                if(upper.size()>high){                    lower.push(upper.top());                    sum-=upper.top()*2;                    upper.pop();                }                //前面的调整可能使顺序错乱,将小的里面的最大值和大的里面的最大值比较                //作交换调整                while(lower.size()&&upper.size()&&lower.top()>upper.top()){                    int u=lower.top(),v=upper.top();                    lower.pop();upper.pop();                    sum=sum+2*u-2*v;                    lower.push(v);upper.push(u);                }                int ans=sum;                //如果个数为奇数,说明中位数为大的里面的最小值,要减掉                if(high>low) ans-=upper.top();                b[i][j]=ans;            }        }        //n^2*k的DP        for(int i=1;i<=n;i++){            for(int j=1;j<=k;j++){                for(int r=0;r<i;r++)                   dp[i][j]=(dp[r][j-1]+b[r+1][i])<dp[i][j]?(dp[r][j-1]+b[r+1][i]):dp[i][j];            }        }        printf("%d\n",dp[n][k]);    }    return 0;}


原创粉丝点击