Estimation[优先队列+DP]

传送门

首先f[i][j] 表示1--i 区间分成j段的最小花费,w[i][j]表示区间i--j的花费

f[i][j]=min(f[i][j],f[x][j-1]+w[x+1][i])

现在是如何快速求w[i][j]的问题

首先知道B因取中位数

其次发现w[i][j] = val[后半部分] - val[前半部分]

用两个优先队列动态维护前一半和后一半就好了

复杂度O(n^2*log(n) + n^2*k)


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define N 2005
#define K 30
using namespace std;
int f[N][K],n,k,a[N],w[N][N];
int main(){
	while(scanf("%d%d",&n,&k) && (n+k)!=0){	
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
		}
		for(int i=1;i<=n;i++){
			priority_queue<int> pre;
           	priority_queue<int,vector<int>,greater<int> > suf;
           	int now=0;
			for(int j=i;j<=n;j++){
           		if(suf.empty()||a[j]>=suf.top()) suf.push(a[j]),now+=a[j];
           		else pre.push(a[j]),now-=a[j];
           		int x1=(j-i+1)>>1,x2=j-i+1-x1;
           		if(pre.size()>x1) suf.push(pre.top()),now+=pre.top()*2,pre.pop();
           		if(suf.size()>x2) pre.push(suf.top()),now-=suf.top()*2,suf.pop();
           		if(x1==x2) w[i][j]=now;
           		else w[i][j]=now-suf.top();
			}
		}
		memset(f,127,sizeof(f));
		for(int i=1;i<=n;i++) f[i][1]=w[1][i];
		for(int i=1;i<=n;i++)
			for(int j=2;j<=k;j++)
				for(int x=1;x<i;x++)
					f[i][j]=min(f[i][j],f[x][j-1]+w[x+1][i]);
		printf("%d\n",f[n][k]);
	}return 0;
}

猜你喜欢

转载自blog.csdn.net/sslz_fsy/article/details/84192935