1. 程式人生 > 其它 >[題解] P2120 [ZJOI2007]倉庫建設 動態規劃 斜率優化

[題解] P2120 [ZJOI2007]倉庫建設 動態規劃 斜率優化

技術標籤:演算法動態規劃演算法c++

[題解] P2120 [ZJOI2007]倉庫建設

洛谷題目連結

首先考慮用動態規劃

f_i​表示從第1個到第i個位置 (第i個位置修建倉庫) 的代價

sum_i表示從第1個到第i個位置的成品總和

dis_i​表示到第i個位置到到第n個位置的距離

s_i表示從1個到第i個位置所有成品運輸到第n個位置的代價

可以列出轉移方程

f_i=min\{f_j+s_i-s_j-(sum_i-sum_j)*dis_i\}_{0 \le j \le i-1}+c_i

其中s_i-s_j-(sum_i-sum_j)*dis_i表示從第j+1個到第i個位置所有成品運到倉庫i的代價

程式碼如下

#include <cstdio>
#include <algorithm>

using namespace std;

const int N=1e6+7;
int n,x[N],p[N],c[N],sum[N],s[N],f[N];
int Q[N],l=1,r=1;

int X(int x){return Q[x]==0?0:-sum[Q[x]];}
int Y(int x){return Q[x]==0?0:f[Q[x]]+s[Q[x]+1];}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d%d%d",&x[i],&p[i],&c[i]);
	for(int i=1;i<=n;i++)x[i]=x[n]-x[i],sum[i]=sum[i-1]+p[i],s[i]=s[i-1]+x[i]*p[i];
	
	for(int i=1;i<=n;i++){
		f[i]=1e9+7;
		for(int j=0;j<i;j++)f[i]=min(f[i],f[j]+s[i]-s[j]-(sum[i]-sum[j])*x[i]+c[i]);
	}
	printf("%d",f[n]);
	return 0;
} 

但是顯然這方法複雜度是O(n^2)無法通過此題

觀察轉移方程,可以發現可以使用斜率優化


f_i=f_j+s_i-s_j-(sum_i-sum_j)*dis_i+c_i

f_i=f_j+s_i-s_j-sum_i*dis_i+sum_j*dis_i+c_i

移項得到

f_j-s_j=-sum_j*dis_i+f_i-s_i+sum_i*dis_i-c_i

x: -sum_j

y: f_j-s_j

k: dis_i

b: f_i-s_i+sum_i*dis_i-c_i

其中x單調不增,可以用斜率優化O(n)解決

程式碼如下

#include <cstdio>
#include <algorithm>

#define int long long

using namespace std;

const int N=1e6+7;
int n,x[N],p[N],c[N],sum[N],s[N],f[N];
int Q[N],l=1,r=1;

int X(int x){return Q[x]==0?0:-sum[Q[x]];}
int Y(int x){return Q[x]==0?0:f[Q[x]]-s[Q[x]];}

signed main(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)scanf("%lld%lld%lld",&x[i],&p[i],&c[i]);
	for(int i=1;i<=n;i++)x[i]=x[n]-x[i],sum[i]=sum[i-1]+p[i],s[i]=s[i-1]+x[i]*p[i];
	
	for(int i=1;i<=n;i++){
		while(l<r&&((X(l)-X(l+1))*x[i]<=(Y(l)-Y(l+1))))++l;
		int j=Q[l];
		f[i]=f[j]+s[i]-s[j]-sum[i]*x[i]+sum[j]*x[i]+c[i],Q[++r]=i;
		while(l+1<r&&(Y(r-2)-Y(r-1))*(X(r-1)-X(r))<=(Y(r-1)-Y(r))*(X(r-2)-X(r-1)))Q[r-1]=Q[r],--r;
	}
	printf("%lld",f[n]);
	return 0;
}