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;
    }
    


  • 相关阅读:
    AD 快捷键
    AD PCB 错误检查
    AD 在 PCB导出封装库
    AD PCB 机械层 画板步骤
    不同频率对PCB材料的要求
    RF 天线长度,通信距离估算
    RF硬件检查注意事项
    影响RSSI的因素
    阅读与思考
    面向对象特性分析
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793914.html
Copyright © 2011-2022 走看看