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