1. 程式人生 > >P2120 [ZJOI2007]倉庫建設 斜率優化dp

P2120 [ZJOI2007]倉庫建設 斜率優化dp

好題 部門 產品 ring clean sizeof cpp put 數學

好題,這題是我理解的第一道斜率優化dp,自然要寫一發題解。首先我們要寫出普通的表達式,然後先用前綴和優化。然後呢?我們觀察發現,x【i】是遞增,而我們發現的斜率也是需要是遞增的,然後就維護一個單調遞增就行了。

放一個證明題解。

設f[i]表示在i點建倉庫的最小費用,易得方程:f[i]=min(f[j]+(x[i]-x[j+1])*p[j+1]+(x[i]-x[j+1])*p[j+2]...) =min(f[j]+c[i]+x[i]*(p[j+1..i])-(x[j+1]*p[j+1]+...+x[i]*p[i]))

設s[i]=p[1]+p[2]+..p[i],ss[i]=x[1]*p[1]+...x[i]*p[i]
f[i]=f[j]+c[i]+x[i]*(s[i]-s[j])-(ss[i]-ss[j])

設j<k即s[j]<s[k],當取k更優時滿足:
f[j]+x[i]*(s[i]-s[j])+ss[j]>f[k]+x[i]*(s[i]-s[k])+ss[k]
x[i]>(f[k]-f[j]+ss[k]-ss[j])/(s[k]-s[j])

設x<y<z,cale(i,j)表示i、j間的斜率
若cale(x,y)>cale(y,z)
 1.x[i]>cale(x,y)>cale(y,z)則z更優
 2.x[i]<cale(x,y),則x更優
因為x[i]遞增,情況1保持不變,情況2可能會變成情況1還是不可能取y
綜上當cale(x,y)>cale(y,z)時可以踢掉y,即維護斜率遞增
題幹:
題目背景

小B的班級數學學到多項式乘法了,於是小B給大家出了個問題:用編程序來解決多項式乘法的問題。
題目描述

L公司有N個工廠,由高到底分布在一座山上。

工廠1在山頂,工廠N在山腳。 由於這座山處於高原內陸地區(幹燥少雨),L公司一般把產品直接堆放在露天,以節省費用。

突然有一天,L公司的總裁L先生接到氣象部門的電話,被告知三天之後將有一場暴雨,於是L先生決定緊急在某些工廠建立一些倉庫以免產品被淋壞。

由於地形的不同,在不同工廠建立倉庫的費用可能是不同的。第i個工廠目前已有成品Pi件,在第i個工廠位置建立倉庫的費用是Ci。

對於沒有建立倉庫的工廠,其產品應被運往其他的倉庫進行儲藏,而由於L公司產品的對外銷售處設置在山腳的工廠N,故產品只能往山下運(即只能運往編號更大的工廠的倉庫),當然運送產品也是需要費用的,假設一件產品運送1個單位距離的費用是1。

假設建立的倉庫容量都都是足夠大的,可以容下所有的產品。你將得到以下數據:

    工廠i距離工廠1的距離Xi(其中X1
=0); 工廠i目前已有成品數量Pi; 在工廠i建立倉庫的費用Ci; 請你幫助L公司尋找一個倉庫建設的方案,使得總的費用(建造費用+運輸費用)最小。 輸入輸出格式 輸入格式: 第一行包含一個整數N,表示工廠的個數。接下來N行每行包含兩個整數Xi, Pi, Ci, 意義如題中所述。 輸出格式: 僅包含一個整數,為可以找到最優方案的費用。

代碼:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include
<queue> #include<algorithm> #include<cstring> using namespace std; #define duke(i,a,n) for(register int i = a;i <= n;i++) #define lv(i,a,n) for(register int i = a;i >= n;i--) #define clean(a) memset(a,0,sizeof(a)) const int INF = 1 << 30; typedef long long ll; typedef double db; template <class T> void read(T &x) { char c; bool op = 0; while(c = getchar(), c < 0 || c > 9) if(c == -) op = 1; x = c - 0; while(c = getchar(), c >= 0 && c <= 9) x = x * 10 + c - 0; if(op) x = -x; } template <class T> void write(T x) { if(x < 0) putchar(-), x = -x; if(x >= 10) write(x / 10); putchar(0 + x % 10); } const int N = 1e6 + 5; int n,m,x[N],q[N],c[N]; ll f[N],ss[N],s[N]; db calc(int j,int k) { return (f[k] - f[j] + ss[k] - ss[j]) * 1.0 / (s[k] - s[j]); } int main() { read(n); duke(i,1,n) { read(x[i]);read(s[i]);read(c[i]); ss[i] = ss[i - 1] + x[i] * s[i]; s[i] += s[i - 1]; } for(int i = 1,l = 0,r = 0;i <= n;i++) { while(l < r && x[i] > calc(q[l],q[l + 1])) l++; f[i] = f[q[l]] + c[i] - ss[i] + ss[q[l]] + x[i] * (s[i] - s[q[l]]); while(l < r && calc(q[r - 1],q[r]) > calc(q[r],i)) r--; q[++r] = i; } printf("%lld\n",f[n]); return 0; }

 

P2120 [ZJOI2007]倉庫建設 斜率優化dp