bzoj 1096 仓库建设 -斜率优化
L公司有N个工厂,由高到底分布在一座山上。如图所示,工厂1在山顶,工厂N在山脚。由于这座山处于高原内
陆地区(干燥少雨),L公司一般把产品直接堆放在露天,以节省费用。突然有一天,L公司的总裁L先生接到气象
部门的电话,被告知三天之后将有一场暴雨,于是L先生决定紧急在某些工厂建立一些仓库以免产品被淋坏。由于
地形的不同,在不同工厂建立仓库的费用可能是不同的。第i个工厂目前已有成品Pi件,在第i个工厂位置建立仓库
的费用是Ci。对于没有建立仓库的工厂,其产品应被运往其他的仓库进行储藏,而由于L公司产品的对外销售处设
置在山脚的工厂N,故产品只能往山下运(即只能运往编号更大的工厂的仓库),当然运送产品也是需要费用的,
假设一件产品运送1个单位距离的费用是1。假设建立的仓库容量都都是足够大的,可以容下所有的产品。你将得到
以下数据:1:工厂i距离工厂1的距离Xi(其中X1=0);2:工厂i目前已有成品数量Pi;:3:在工厂i建立仓库的费用
Ci;请你帮助L公司寻找一个仓库建设的方案,使得总的费用(建造费用+运输费用)最小。
Input
第一行包含一个整数N,表示工厂的个数。接下来N行每行包含两个整数Xi, Pi, Ci, 意义如题中所述。
Output
仅包含一个整数,为可以找到最优方案的费用。
Sample Input
3 0 5 10 5 3 100 9 6 10
Sample Output
32
Hint
在工厂1和工厂3建立仓库,建立费用为10+10=20,运输费用为(9-5)*3 = 12,总费用32。如果仅在工厂3建立仓库,建立费用为10,运输费用为(9-0)*5+(9-5)*3=57,总费用67,不如前者优。
【数据规模】
对于100%的数据, N ≤1000000。 所有的Xi, Pi, Ci均在32位带符号整数以内,保证中间计算结果不超过64位带符号整数。
题不是很难,dp方程推错了两次(每次都是从n往前),于是无限WA...第三次终于推对了。。(说完一堆废话赶快写正解)
朴素dp的方程(公式编辑器坏了,只能用画图了,请谅解)
$f\left [ i \right ] = \min\left \{ f\left [ j \right ] + \sum_{k = j + 1}^{i - 1}\left ( x_{i} - x_{k} \right )p_{k} \right \} + c_{i}$
一看是三维,死得没有悬念,只能想办法优化。Sigma一在更不好优化,只能先展开
发现P的求和和xP的求和都是可以用前缀和搞定的,于是设,sump[i] = P1 + P2 + ... + Pi,sumxp[i] = x1P1 + x2P2 + ... + xiPi
于是方程变成 f[i] = min{f[j] + xi(sump[i - 1] - sump[j]) - sumxp[i - 1] + sump[j]} + Ci
现在假设能转移到状态i的有两个状态j, k(j < k),如果j比k优,那么
f[j] + xi(sump[i - 1] - sump[j]) - sumxp[i - 1] + sump[j] < f[k] + xi(sump[i - 1] - sump[k]) - sumxp[i - 1] + sump[k]
拆括号化简
f[j] - xisump[j] + sump[j] < f[k] - xisump[k] + sump[k]
右边保留和i有关的单项式
(f[j] + sump[j]) - (f[k] + sump[k]) < xi(sump[j] - sump[k])
移项(还是注意不等号的方向)
于是又愉快地得到了斜率方程,对于状态i,(f[i] + sump[i])作为纵坐标,sump[i]作为横坐标,删掉上凸点,维护一条斜率递增的折线。
Code
1 /** 2 * bzoj 3 * Problem#1096 4 * Accepted 5 * Time:2312ms 6 * Memory:36464k 7 */ 8 #include<iostream> 9 #include<sstream> 10 #include<cstdio> 11 #include<cmath> 12 #include<cstdlib> 13 #include<cstring> 14 #include<cctype> 15 #include<queue> 16 #include<set> 17 #include<map> 18 #include<stack> 19 #include<vector> 20 #include<algorithm> 21 #ifdef WIN32 22 #define AUTO "%I64d" 23 #else 24 #define AUTO "%lld" 25 #endif 26 using namespace std; 27 typedef bool boolean; 28 #define smin(a, b) (a) = min((a), (b)) 29 #define smax(a, b) (a) = max((a), (b)) 30 template<typename T> 31 inline void readInteger(T& u){ 32 char x; 33 int aFlag = 1; 34 while(!isdigit((x = getchar())) && x != '-'); 35 if(x == '-'){ 36 aFlag = -1; 37 x = getchar(); 38 } 39 for(u = x - '0'; isdigit((x = getchar())); u = u * 10 + x - '0'); 40 ungetc(x, stdin); 41 u *= aFlag; 42 } 43 44 template<typename T> 45 class IndexedDeque{ 46 public: 47 T* list; 48 int pfront; 49 int prear; 50 IndexedDeque():list(NULL), pfront(0), prear(0){ } 51 IndexedDeque(int size):pfront(0), prear(0){ 52 list = new T[size]; 53 } 54 void push_front(T x){ list[--pfront] = x; } 55 void push_back(T x) { list[prear++] = x; } 56 void pop_front() { ++pfront; } 57 void pop_back() { --prear; } 58 T front() { return list[pfront]; } 59 T rear() { return list[prear - 1]; } 60 T& operator [](int pos){ return list[pfront + pos]; } 61 int size() { return prear - pfront; } 62 }; 63 64 template<typename T> 65 inline void aalloc(T*& array, int size){ array = new T[(const int)(size + 1)]; } 66 67 int n; 68 long long* sump; 69 long long* sumxp; 70 int* c; 71 int* x; 72 long long* f; 73 IndexedDeque<int> que; 74 75 long long y_pos(int i) { return f[i] + sumxp[i]; } 76 double slope(int j, int k) { return (y_pos(j) - y_pos(k)) * 1.0 / (sump[j] - sump[k]); } 77 double cmpSlope(int i, int j, int k) { return slope(j, k) - x[i]; } 78 79 inline void init() { 80 readInteger(n); 81 aalloc(sump, n); 82 aalloc(sumxp, n); 83 aalloc(c, n); 84 aalloc(x, n); 85 aalloc(f, n + 1); 86 sump[0] = sumxp[0] = x[0] = 0; 87 for(int i = 1, p; i <= n; i++){ 88 readInteger(x[i]); 89 readInteger(p); 90 readInteger(c[i]); 91 sump[i] = sump[i - 1] + p; 92 sumxp[i] = sumxp[i - 1] + x[i] * 1LL * p; 93 } 94 } 95 96 inline void solve() { 97 que = IndexedDeque<int>(n + 5); 98 f[0] = 0; 99 que.push_back(0); 100 for(int i = 1; i <= n; i++) { 101 while(que.size() > 1 && cmpSlope(i, que[0], que[1]) < 0) que.pop_front(); 102 int j = que.front(); 103 f[i] = f[j] + x[i] * (sump[i - 1] - sump[j]) - sumxp[i - 1] + sumxp[j] + c[i]; 104 while(que.size() > 1 && slope(que[que.size() - 2], que[que.size() - 1]) >= slope(que[que.size() - 1], i)) que.pop_back(); 105 que.push_back(i); 106 } 107 printf(AUTO"\n", f[n]); 108 } 109 110 int main() { 111 init(); 112 solve(); 113 return 0; 114 }