[Bzoj1597][Usaco2008 Mar]土地购买(斜率优化)
因为题目说可以分组,并且是求最值,所以斜率优化应该是可以搞的,现在要想怎么排序使得相邻的数在一个组中最优。
我们按照宽$w$从小到大,高$h$从小到大排序。这时发现可以筛掉一些一定没有贡献的土地,什么样的土地没有贡献呢?这样的:$h[i]<=h[j]\& \&w[i]<=w[j]$,此时i没有贡献。
所以排序并筛掉无用的土地后,剩余的土地是按照$w[i]< w[j]< w[k]\& \&h[i]> h[j]>h[k]$ $(i<j<k)$
这时候我们的最优分组一定是选择连续的土地为一组。因为如果i和k一组,j一组,则此时的花费是$h[i]*w[k]+h[j]*w[j]$
而选择$i,j,k$一组,则花费为$h[i]*w[k]$
所以此时有$O(n^{2})$的$dp$:
$dp[i]$为前$i$块土地的最少花费,$dp[i]=max(dp[i],dp[j]+h[j+1]*w[i])$。
但是复杂度不允许QAQ
所以推式子:
设$k<j<i$,且i从j转移比从k转移更优。
$dp[j]+h[j+1]*w[i]\leq dp[k]+h[k+1]*w[i]$
$dp[j]-dp[k]\leq (h[k+1]-h[j+1])*w[i]$
$\tfrac{dp[j]-dp[k]}{h[k+1]-h[j+1]}\leq w[i]$
$\tfrac{dp[j]-dp[k]}{h[j+1]-h[k+1]}\geq- w[i]$
将$(h[j+1],dp[j]),(h[k+1],dp[k])$看成二维平面的点,因为$k<j\& \&h[k+1]>h[j+1]$,所以点集应该是从左往右。
维护一个单调队列,如果当前点为$i$,队首为$L$,则如果$L$没有$L+1$到$i$更优,则队首出队。当前最优点为队首。同时还要维护队尾。
PS:因为以前吃过精度的坑,所以写斜率优化基本是移相相乘。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn = 5e4 + 100; 5 struct node { 6 ll w, h; 7 }a[maxn], b[maxn]; 8 bool cmp(node x, node y) { 9 return x.w == y.w ? x.h < y.h : x.w < y.w; 10 } 11 ll dp[maxn]; int q[maxn]; 12 ll check1(int j, int k) { 13 return dp[j] - dp[k]; 14 } 15 ll check2(int j, int k) { 16 return b[j + 1].h - b[k + 1].h; 17 } 18 int main() { 19 int n, cnt = 0; 20 scanf("%d", &n); 21 for (int i = 1; i <= n; i++) 22 scanf("%lld%lld", &a[i].w, &a[i].h); 23 sort(a + 1, a + 1 + n, cmp); 24 for (int i = 1; i <= n; i++) { 25 while (cnt != 0 && b[cnt].h <= a[i].h) 26 cnt--; 27 b[++cnt] = a[i]; 28 } 29 int l = 0, r = 0; 30 for (int i = 1; i <= cnt; i++) { 31 while (l < r && check1(q[l], q[l + 1]) >= -b[i].w * check2(q[l], q[l + 1])) 32 l++; 33 dp[i] = dp[q[l]] + b[q[l] + 1].h * b[i].w; 34 while (l < r && check1(q[r - 1], q[r]) * check2(q[r], i) <= check1(q[r], i) * check2(q[r - 1], q[r])) 35 r--; 36 q[++r] = i; 37 } 38 printf("%lld\n", dp[cnt]); 39 }