1. 程式人生 > >[ZJOI2007] 倉庫建設

[ZJOI2007] 倉庫建設

urn min () algo har con col print 動態規劃

傳送門:>HERE<

題意:有n個地點,每個地點有貨物P[i]個,距離起點(地點0)的距離為x[i]。在每個地點建立倉庫需要費用c[i],現在需要在某些地點建設倉庫,從而將貨物轉移到倉庫裏。規定只能從編號小的地點轉移到編號大的地點,同時轉移的費用的是路程*貨物數量。求最小的總費用(總費用=建設費用+轉移費用)

解題思路:

  動態規劃+斜率優化

  令$f[i]$表示在地點i建設倉庫,並且1~i的貨物都已經全部處理好了。可以得到$O(n^2)$的狀態轉移方程$$f[i] = Min\{f[j] + \sum\limits_{k=1}^{i-1}(x[i]-x[k])*p[k] + c[i]\}$$

  維護前綴和P[i]為數組p的前綴和,g[i]為x[i]*p[i]的前綴和。得到$$f[i] = f[j] + (P[i-1]-P[j])*x[i] - (g[i-1]-g[j]) + c[i]$$

轉化為一次函數形式$$f[j]+P[i-1]*x[i]-g[i-1]+g[j]+c[i] = x[i] * P[j] + f[i] \Longleftrightarrow y = kx + b$$

去掉沒用的項,得$$f[j]+g[j] = x[i] * P[j] + f[i]$$

  坐標為$(P[j], f[j]+g[j])$

  正常做就行了

  這題主要難在f[i]的構想上,斜率優化這一步並沒有什麽坑點

Code

  long long

/*By QiXingzhi*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#define  r  read()
#define  Max(a,b)  (((a)>(b)) ? (a) : (b))
#define  Min(a,b)  (((a)<(b)) ? (a) : (b))
using namespace
std; typedef long long ll; #define int long long const int MAXN = 1000010; const int INF = 1061109567; inline int read(){ int x = 0; int w = 1; register int c = getchar(); while(c ^ - && (c < 0 || c > 9)) c = getchar(); if(c == -) w = -1, c = getchar(); while(c >= 0 && c <= 9) x = (x << 3) +(x << 1) + c - 0, c = getchar(); return x * w; } int n,m,h,t; int x[MAXN],p[MAXN],c[MAXN],g[MAXN],P[MAXN],q[MAXN],f[MAXN]; inline double X(int i){ return P[i]; } inline double Y(int i){ return f[i] + g[i]; } inline double Slope(int i, int j){ return (double)(Y(i)-Y(j)) / (double)(X(i)-X(j)); } main(){ // freopen(".in","r",stdin); n = r; for(int i = 1; i <= n; ++i){ x[i] = r, p[i] = r, c[i] = r; P[i] = P[i-1] + p[i]; g[i] = g[i-1] + x[i] * p[i]; } for(int i = 1; i <= n; ++i){ while(h<t && Slope(q[h],q[h+1]) < x[i]) ++h; f[i] = f[q[h]] + (P[i-1]-P[q[h]])*x[i] - g[i-1] + g[q[h]] + c[i]; while(h<t && Slope(q[t-1],q[t]) > Slope(q[t],i)) --t; q[++t] = i; } printf("%lld", f[n]); return 0; }

[ZJOI2007] 倉庫建設