BZOJ 1597 [Usaco2008 Mar]土地购买:斜率优化dp
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1597
题意:
有n块矩形土地,长为a[i],宽为b[i]。
FJ想要将这n块土地全部买下来。
土地可以分组购买。
若有某一些土地被分到了一组,则将这一组土地全部买下的花费为他们的max(a[i])*max(b[i])。
问你最小总花费是多少。
题解:
首先,如果一块土地能够被另一块土地完全包含(即长宽都比另一个小),那么被包含的那块土地可以忽略不计。
然后贪心地考虑如何使花费最小:
如果要使花费尽可能小,则分在同一组中矩形的长宽差距应尽可能地小。
所以将长a[i]作为第一关键字,将宽b[i]作为第二关键字排序。
此时矩形的长a[i]一定是非降的,那么对于两个矩形i和j(i<j),如果有b[i]<=b[j],则j一定能完全包含i。
利用这一特性将所有能被包含的矩形去掉。
此时矩形的宽b[i]就变成了严格递减的了。
显然,此时分到一组中的矩形必须是连续的一段区间,才有可能最优。
然后就可以dp了。
表示状态:
dp[i]表示已经购买了前i块土地的最小花费。
找出答案:
假设去掉能被包含的矩形之后,矩形总数为tot。
ans = dp[tot]
如何转移:
dp[i] = min dp[j] + a[i]*b[j+1]
边界条件:
dp[0] = 0
斜率优化:
设j < k,且k的决策更优。
则:dp[j] + a[i]*b[j+1] > dp[k] + a[i]*b[k+1]
整理得:(dp[k]-dp[j])/(b[j+1]-b[k+1]) < a[i]
所以slope(i,j) = (dp[i]-dp[j])/(b[j+1]-b[i+1])
由于a[i]非降,所以用单调队列维护下凸壳即可。
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <algorithm> 5 #define MAX_N 50005 6 7 using namespace std; 8 9 struct Rect 10 { 11 long long a,b; 12 Rect(long long _a,long long _b) 13 { 14 a=_a; b=_b; 15 } 16 Rect(){} 17 friend bool operator < (const Rect &x,const Rect &y) 18 { 19 return x.a!=y.a ? x.a<y.a : x.b<y.b; 20 } 21 }; 22 23 int n; 24 int q[MAX_N]; 25 long long dp[MAX_N]; 26 Rect x[MAX_N]; 27 28 void read() 29 { 30 cin>>n; 31 for(int i=1;i<=n;i++) 32 { 33 cin>>x[i].a>>x[i].b; 34 } 35 } 36 37 inline double slope(int i,int j) 38 { 39 return (double)(dp[i]-dp[j])/(x[j+1].b-x[i+1].b); 40 } 41 42 void work() 43 { 44 sort(x+1,x+1+n); 45 int tot=0; 46 for(int i=1;i<=n;i++) 47 { 48 while(tot && x[i].b>=x[tot].b) tot--; 49 x[++tot]=x[i]; 50 } 51 x[tot+1]=Rect(0,0); 52 int l=1,r=1; 53 q[1]=0,dp[0]=0; 54 for(int i=1;i<=tot;i++) 55 { 56 while(l<r && slope(q[l],q[l+1])<=x[i].a) l++; 57 dp[i]=dp[q[l]]+x[i].a*x[q[l]+1].b; 58 while(l<r && slope(q[r],i)<slope(q[r-1],q[r])) r--; 59 q[++r]=i; 60 } 61 cout<<dp[tot]<<endl; 62 } 63 64 int main() 65 { 66 read(); 67 work(); 68 }