zoukankan      html  css  js  c++  java
  • 【HDU4261】Estimation-DP+优先队列优化

    测试地址:Estimation

    题目大意:将一个长度为n的数列A分成k个连续的段,每一段给一个B值(即如果A[i]和A[j]属于同一段,则B[i]=B[j]),使得Σ|A[i]-B[i]|最小,输出这个最小值。

    做法:容易想到DP做法。设f[i][j]为数列前i个数分成j个段的最小的Σ|A[i]-B[i]|,则易得出状态转移方程:

    f[i][j]=min(f[r][j-1],sum[r+1][i])  (0≤r<i)

    其中sum[a][b]表示区间[a,b]分成一段的情况下这个区间中最小的Σ|A[i]-B[i]|。

    这个DP的时间复杂度是O(n^2*k)的,而裸求sum的时间复杂度太高,需要预处理。

    我们知道,对于一个段来说,B值取它们的中位数一定最优。维护两个优先队列lower和upper,分别为比中位数小的部分和比中位数大的部分,lower是值越大优先级越大,upper是值越小优先级越大,用s表示upper中元素之和与lower中元素之和的差。维护lower的大小总是(r-l+1)/2,upper的大小总是(r-l+1)-(lower大小)(l,r为所求区间的左右端点坐标),这样的话,如果区间内元素个数是奇数,则upper中最小值就是区间的中位数,此时sum[l][r]=s-upper中最小值;如果区间内元素个数是偶数,则中位数可以为upper中最小值和lower中最大值之间的任何数,此时sum[l][r]=s。

    那么,怎么使lower和upper的大小是我们想要的值呢?实际上很简单,如果lower的大小大于我们想要的值,则将lower中的最大值取出放到upper中,如果还大就继续这样操作。反之,如果upper的大小大于我们想要的值,则将upper中的最小值取出放到lower中,如果还大就继续这样操作。由于lower和upper的定义,所以实现这样的操作是很方便的。

    以下是本人代码(在hdu上无法通过编译,难道是禁用了priority_queue?求高人指点迷津):

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <vector>
    #include <set>
    #define inf 1000000000
    using namespace std;
    int n,k,a[2005],sum[2005][2005],f[2005][30]={0};
    
    priority_queue<int>lower;
    priority_queue<int,vector<int>,greater<int> >upper;
    
    void calc_sum()
    {
      for(int i=1;i<=n;i++)
      {
        while(!lower.empty()) lower.pop();
    	while(!upper.empty()) upper.pop();
    	int s=0;
    	for(int j=i;j<=n;j++)
    	{
    	  if (lower.empty()||a[j]<=lower.top()) {lower.push(a[j]);s-=a[j];}
    	  else {upper.push(a[j]);s+=a[j];}
    	  unsigned int low=(j-i+1)/2,upp=(j-i+1)-low;
    	  if (lower.size()>low)
    	  {
    	    upper.push(lower.top());
    	    s+=2*lower.top();
    		lower.pop();
    	  }
    	  if (upper.size()>upp)
    	  {
    	    lower.push(upper.top());
    	    s-=2*upper.top();
    		upper.pop();
    	  }
    	  while(lower.size()&&upper.size()&&lower.top()>upper.top())
    	  {
    	    int u=lower.top(),v=upper.top();
    		lower.pop();upper.pop();
    		lower.push(v);upper.push(u);
    		s=s+2*u-2*v;
    	  }
    	  int ans=s;
    	  if (upp>low) ans-=upper.top();
    	  sum[i][j]=ans;
    	}
      }
    }
    
    int main()
    {
      while(scanf("%d%d",&n,&k)!=EOF&&n+k)
      {
        for(int i=1;i<=n;i++)
    	  scanf("%d",&a[i]);
    	calc_sum();
    	for(int i=1;i<=n;i++)
    	  for(int j=0;j<=k;j++)
    	    f[i][j]=inf;
        for(int i=1;i<=n;i++)
    	  for(int j=1;j<=k;j++)
    	    for(int r=0;r<i;r++)
    		  f[i][j]=min(f[i][j],f[r][j-1]+sum[r+1][i]);
    	printf("%d
    ",f[n][k]);
      }
      
      return 0;
    }
    


  • 相关阅读:
    DIY 作品 及 维修 不定时更新
    置顶,博客中所有源码 github
    openwrt PandoraBox PBR-M1 极路由4 HC5962 更新固件
    使用 squid 共享 虚拟专用网至局域网
    第一次参加日语能力测试 N5
    libx264 libfdk_aac 编码 解码 详解
    开发RTSP 直播软件 H264 AAC 编码 live555 ffmpeg
    MFC Camera 摄像头预览 拍照
    http2 技术整理 nginx 搭建 http2 wireshark 抓包分析 server push 服务端推送
    plist 图集 php 批量提取 PS 一个个切
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793914.html
Copyright © 2011-2022 走看看