UVA 12524 Arranging Heaps 动态规划 斜率优化

定义dp[i][j]  代表在i这个地方放一个Heap , 从1到i这个区间一共放了n个heap,最小的cost。

很容易想到dp转移方程:dp[i][j]  = min {  dp[k][j-1] + cost ( k ,i) }   (  1<=K<i  , cost[(k,i) 代表把 k+1到i区间中的所有heap移动到第 i这个heap的cost。

这是2d/1d的dp 还有优化余地。


展开转移方程:    dp[i][j] = min ( dp[k][j-1] +  (p[i].h - p[i].h) * p[i].w +  ( p[i].h - p[i-1].h ) * p[i-1].w + ... + (p[i].h - p[k+1].h) * p[k+1].w )

定义                        sw[i]  = sum(p[k].w)   (1<=k<=i)         ;   sm[i] = sum(p[k].h * p[k].w)    (1<=k<=i)

所以                        dp[i][j] = min ( dp[k][j-1]  + p[i].h *  ( sw(i) - sw(k)) - ( sm(i) - sm(k) );

再转化一下 就是   dp[k][j-1] + sm[k] =  p[i].h * sw[k]  + dp[i][j] +  sm[i] -sw[i]*p[i].h ;

令                            y = dp[k][j-1] + sm[k] ; x = sw[k]; a = p[i].h ;  b = dp[i][j] +  sm[i] -sw[i]*p[i].h;

所以                        y = a * x + b;

其中b就是我们间接要求的,剩下就是用栈维护一个凸包,将复杂度降为 n^2

#include <cstdio>   
#include <iostream>
#include <queue>
#include <string.h>   
#include <cmath>
#include <vector>
#include <algorithm>   
using namespace std;
typedef long long ll;
#define sfint(x) scanf("%d",&x)
#define sfint2(x,y) scanf("%d%d",&x,&y)
#define sfint3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define sfstr(c) scanf("%s",c)
#define sfdl(x) scanf("%lf",&x)
#define sfll(x) scanf("%I64d",&x)
#define sfch(c) scanf("%c",&c)
#define fr(i,s,n) for(int i=s;i<n;++i)
#define cl(a) memset(a,0,sizeof(a))
int n,K;
const ll inf = 1LL<<60;
ll dp[1010][1010];

struct STA{
	struct Sta{
		ll x,y;
		Sta(ll a,ll b):x(a),y(b){}
		Sta(){}
	}s[1010];
	int top,now;
	void init(){
		top = -1;
		now = 0;
	}
	void P(ll a,ll b){
		if(top <1) s[++top] = Sta(a,b);
		else{
			while(1){
				if(top == 0){
					s[++top] = Sta(a,b);
					break;
				}
				ll y1 = b-s[top].y;
				ll x1 = a-s[top].x;
				ll y2 = s[top].y - s[top-1].y;
				ll x2 = s[top].x - s[top-1].x;
				if( y1 * x2 > y2 * x1){
					s[++top] = Sta(a,b);
					break;
				}
				else{
					top --;
				}
			}
		}
	}
	Sta Top(){
		return s[top];
	}
}S;

struct P{
	ll h,w;
	void read(){
		scanf("%lld%lld",&h,&w);
	}
}p[1010];

ll sw[1010],sm[1010];
void init(){
	sw[0] = 0;sm[0] = 0;
	fr(i ,1  ,n+1){
		sw[i] = sw[i-1] + p[i].w;
		sm[i] = sm[i-1] + p[i].w * p[i].h;
	}
}

double cal(int x){
	return double (S.s[x+1].y - S.s[x].y)/ double(S.s[x+1].x - S.s[x].x );
}
int main(){
	while(sfint2(n,K)!=EOF){
		fr(i ,1  ,n+1){
			p[i].read();
		}
		init();
		fr(i , 0 , n+1){
			fr(j ,0 , K+1){
				dp[i][j] = inf;
			}
		}
		dp[1][1] = 0;
		S.init();
		fr(i ,2, n+1){
			dp[i][1] = dp[i-1][1] + sw[i-1] * (p[i].h - p[i-1].h);	
		}
		for(int j = 2;j<=K;++j){
			for(int i = j;i<n+1;++i){
				S.P(sw[i-1] ,dp[i-1][j-1]+sm[i-1] );
				while( S.now<S.top &&  cal(S.now) < double(p[i].h) ){
					S.now++;
				}
				dp[i][j] = S.s[S.now].y - S.s[S.now].x * p[i].h + sw[i]*p[i].h -sm[i];
				
			}
			S.init();
		}
		printf("%lld\n",dp[n][K]);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值