其他分享
首页 > 其他分享> > HDU - 4261 Estimation(线性DP + 堆优化动态求中位数)

HDU - 4261 Estimation(线性DP + 堆优化动态求中位数)

作者:互联网

链接HDU - 4261 Estimation

题意:

给出长度为NNN(1N20001\le N\le 20001≤N≤2000)的序列A1,A2,,ANA_1,A_2,\cdots,A_NA1​,A2​,⋯,AN​,要求将其分为KKK(1Kmin{25,N}1\le K\le \min\{25,N\}1≤K≤min{25,N})段,并对每段确定一个值BjB_jBj​(1jK1\le j\le K1≤j≤K),使得AiBj\sum|A_i-B_j|∑∣Ai​−Bj​∣最小,其中iii与jjj的关系由划分决定。

要求输出AiBj\sum|A_i-B_j|∑∣Ai​−Bj​∣的最小值



分析:

dp的方程并不难想,以处理序列当前的 iii个 作为 阶段,以将前iii个 划分为jjj段 作为 状态,可得:

F(i,j)F(i,j)F(i,j)表示A1,A2,,AiA_1,A_2,\cdots,A_iA1​,A2​,⋯,Ai​划分为jjj段时答案的最小值

状态转移方程F(i,j)=min0ki1{F(k,j1)+S(k+1,i)}                1iN,1jKF(i,j)=\min\limits_{0\le k\le i-1}\{F(k,j-1)+S(k+1,i)\}\;\;\;\;\;\;\;\;1\le i\le N,1\le j\le KF(i,j)=0≤k≤i−1min​{F(k,j−1)+S(k+1,i)}1≤i≤N,1≤j≤K
其中 S(u,v)S(u,v)S(u,v)表示 Au,Au+1,AvA_u,A_{u+1},\cdots A_vAu​,Au+1​,⋯Av​作为一段时,取合适的BBB得到的uivAiB\displaystyle\sum_{u\le i\le v}|A_i-B|u≤i≤v∑​∣Ai​−B∣的最小值

初值F(0,0)=0F(0,0)=0F(0,0)=0,其他均为\infin∞;
目标F(N,K)F(N,K)F(N,K)

S(u,v)S(u,v)S(u,v)可以在O(1)O(1)O(1)的时间复杂度内求得,则时间复杂度为O(N2K)O(N^2 \cdot K)O(N2⋅K)。


考虑预处理得到所有的S(i,j)S(i,j)S(i,j),显然对于任意序列,要找到一个数,使得该数与序列中所有数的差值绝对值之和最小,该数必定是 序列的中位数

动态维护一个有序序列,即可快速求得中位数,易想到利用堆来维护,但是堆并不支持随机存取,故此处要用“ 对顶堆 ”来处理。

利用一个 小根堆存储当前序列中值较大的一半元素(在堆内从小到大排列),利用一个 大根堆 来存储 当前序列中值较小的一半元素(在堆内从大到小排列),这样将元素逐个与堆顶比较后加入小根堆/大根堆,并 同时对两个堆进行修正,保证其大小差值不超过1(由于“对顶堆”的大根堆和小根堆的堆顶是 “ 相连的 ”,只需将其中一个堆的堆顶元素取出并放入另一个堆即可),这样 中位数必定由小根堆和大根堆的堆顶决定

为求得S(i,j)S(i,j)S(i,j),每次固定iii,遍历jjj,还需要维护两个堆内元素的和,从而直接求得S(i,j)S(i,j)S(i,j),故预处理的时间复杂度为O(N2log2N)O(N^2\log_2 N)O(N2log2​N)。



代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int INF=0x3f3f3f3f;
const int maxn=2e3+10;
int N,K,A[maxn];
int F[maxn][30],S[maxn][maxn];
int main()
{
    while(scanf("%d %d",&N,&K)&&(N||K))
    {
        for(int i=1;i<=N;i++)
            scanf("%d",&A[i]);
        for(int i=1;i<=N;i++)
        {
            priority_queue<int,vector<int>,greater<int> > h1;   //小根堆,存较大的数
            priority_queue<int> h2;                             //大根堆,存较小的数
            int s1=0,s2=0;                                      //小根堆的和,大根堆的和
            for(int j=i;j<=N;j++)
            {
                if(h1.empty()||A[j]>=h1.top())   //若小根堆为空,或A[j]大于等于小根堆堆顶
                {                                //将A[j]加入小根堆
                    h1.push(A[j]);
                    s1+=A[j];
                }
                else                             //否则将A[j]加入大根堆
                {
                    h2.push(A[j]);
                    s2+=A[j];
                }

                //进行修正,保证小根堆大小==大根堆大小,或小根堆大小==大根堆大小+1
                while(h1.size()>h2.size()+1)
                {
                    h2.push(h1.top());
                    s2+=h1.top();
                    s1-=h1.top();
                    h1.pop();
                }
                while(h1.size()<h2.size())
                {
                    h1.push(h2.top());
                    s1+=h2.top();
                    s2-=h2.top();
                    h2.pop();
                }

                if(h1.size()==h2.size())
                    S[i][j]=s1-s2;
                else
                    S[i][j]=s1-s2-h1.top();
            }
        }
        memset(F,0x3f,sizeof(F));
        F[0][0]=0;
        for(int i=1;i<=N;i++)
            for(int j=1;j<=K;j++)
                for(int k=0;k<=i-1;k++)
                    F[i][j]=min(F[i][j],F[k][j-1]+S[k+1][i]);
        printf("%d\n",F[N][K]);
    }
    return 0;
}

墓华 发布了213 篇原创文章 · 获赞 40 · 访问量 2万+ 私信 关注

标签:4261,HDU,le,大根堆,int,h1,Ai,小根堆,Estimation
来源: https://blog.csdn.net/Ratina/article/details/104094780