BZOJ 1096 ZJOI2007 仓库设计 斜率优化dp
太高兴了,这是我第一次自己独立思考的斜率优化dp,从头到尾都是自己想的。(相信自己,能行的,不过也做了40分钟了)。 这道题目还好吧! 看到之后第一反应是想设从工厂0运到工厂i 总共需要 tot[i] 的费用, 用 p[i] 表示从山顶到工厂 i 总共的产品数, 再用 x[i] 表示从工厂0到工厂 i 的距离, 那么状态转移方程就是 f[i] = min{f[j] + tot[i] - tot[j] - p[j] * (x[i] - x[j] ) + c[i] } ,很明显由于数据有 n <= 1000000 不能 n ^ 2 的朴素算法, 要进行优化。 先看一下状态转移方程, 除了 f[j] 不定之外, 其他的在数据给出时都已确定(换句话说,满足无后效性,只要保证之前处理f[i]时,f[i]是最小就好),可以考虑斜率优化。
那么设 j < k , 目前正在处理 f[i]; 如果 j 比 k 差的话, 那么 f[j] + tot[i] - tot[j] - p[j] * (x[i] - x[j] ) >= f[k] + tot[i] - tot[k] - p[k] * (x[i] - x[k])
移项化简得当 f[j] - f[k] - tot[j] + tot[k] + p[j] * x[j] - p[k] * x[k] >= x[i] * (p[j] - p[k]) 时 j 比 k 差。
又因为求的是最小值所以要维护一个下凸函数。加油,努力。(1700毫秒)
1 #include<cstdio> 2 #include<iostream> 3 #define rep(i,j,k) for(int i = j; i <= k; i++) 4 #define maxn 1000005 5 #define ll long long 6 using namespace std; 7 8 int read() 9 { 10 int s = 0, t = 1; char c = getchar(); 11 while( !isdigit(c) ){ 12 if( c == '-' )t = -1; c = getchar(); 13 } 14 while( isdigit(c) ){ 15 s = s * 10 + c -'0'; c = getchar(); 16 } 17 return s * t; 18 } 19 20 ll tot[maxn] = {0}, f[maxn] = {0}, c[maxn] = {0}, p[maxn] = {0}, x[maxn] = {0}; 21 int q[maxn] = {0}; 22 23 ll S(int k,int j) 24 { 25 return f[j] - f[k] + p[j] * x[j] - p[k] * x[k] + tot[k] - tot[j]; 26 } 27 28 ll G(int k,int j) 29 { 30 return p[j] - p[k]; 31 } 32 33 int main() 34 { 35 int n = read(); 36 rep(i,1,n){ 37 x[i] = read(), p[i] = read(), c[i] = read(); p[i] += p[i-1]; 38 } 39 rep(i,1,n){ 40 tot[i] = p[i-1] * (x[i]-x[i-1]) + tot[i-1]; 41 } 42 int l = 0, r = 0; q[r] = 0; 43 rep(i,1,n){ 44 while( l < r && S(q[l+1],q[l]) >= G(q[l+1],q[l]) * x[i] ) l++; 45 int j = q[l]; 46 f[i] = f[j] + tot[i] - tot[j] + c[i] - p[j] * (x[i]-x[j]); 47 while( l < r && S(q[r],q[r-1]) * G(i, q[r]) >= S(i,q[r]) * G(q[r],q[r-1]) ) r--; 48 q[++r] = i; 49 } 50 cout<<f[n]<<endl; 51 return 0; 52 }
————————————————