[BZOJ] 1597: [Usaco2008 Mar]土地购买
1597: [Usaco2008 Mar]土地购买
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 5308 Solved: 1967
[Submit][Status][Discuss]
Description
农夫John准备扩大他的农场,他正在考虑N (1 <= N <= 50,000) 块长方形的土地. 每块土地的长宽满足(1 <= 宽 <= 1,000,000; 1 <= 长 <= 1,000,000). 每块土地的价格是它的面积,但FJ可以同时购买多快土地. 这些土地的价格是它们最大的长乘以它们最大的宽, 但是土地的长宽不能交换. 如果FJ买一块3x5的地和一块5x3的地,则他需要付5x5=25. FJ希望买下所有的土地,但是他发现分组来买这些土地可以节省经费. 他需要你帮助他找到最小的经费.
Input
* 第1行: 一个数: N
* 第2..N+1行: 第i+1行包含两个数,分别为第i块土地的长和宽
Output
* 第一行: 最小的可行费用.
Sample Input
100 1
15 15
20 5
1 100
输入解释:
共有4块土地.
Sample Output
HINT
FJ分3组买这些土地: 第一组:100x1, 第二组1x100, 第三组20x5 和 15x15 plot. 每组的价格分别为100,100,300, 总共500.
Source
Analysis
想了想我还是比较喜欢爆炸OJ的排版
UOJ = = 复制过来整个乱了,样例数据的格式还会感染下一行
(如果你们试试博客园复制一些奇怪格式的样例数据你们就知道我在说什么了)
咳咳
斜率优化DP
我们要购买前 i 块土地所用的最小价格设为 DP[i]
然后我们把这些土地排个序,维护一个单调队列,满足 x 单调递增(第一关键字) y 单调递减(第二关键字)
因为我们知道小土地是可以被大土地覆盖的,上面的排序请自行思考
那么显然 DP[i] = min( DP[j] + y[j+1]*x[i] )
j+1 是因为前 j 都已经买完了,现在要买是决策第 j+1 个
那么对于 j > k 且假设 j 比 k 优,我们有:
DP[j] +x[i]*y[j+1] < DP[k] + x[i]*y[k+1]
经过一些简易的数学推导我们得到:
(DP[j]-DP[k])/(y[k+1]-y[j+1]) = val
而val < x[i]
为什么这里的不等号没有改变呢?
我们知道 j > k ,而 y 经过排序单调递减,所以易证 y[k+1] - y[j+1] > 0
因此只要符合 不等式 就可以说明当前的 j 比 k 更优
那么这个不等式有什么用呢?
用于单调队列优化DP的判定
维护另外一个单调队列:这个单调队列储存元素为土地的下标,维护这些土地下标的val的单调性( j 和 k 分别指代队列中相邻的两个元素)
然后每次要取DP[j]的时候,就从队尾往队头走(据说我的队头队尾和你们不一样),找到最大的满足 val < x[i] 的元素
然后计算
然后从队头往内砍元素:直到 i 在队头的时候队列满足单调性
具体什么情况,自行推导对于这道题非常重要
Code
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #define maxn 1000000 5 using namespace std; 6 7 long long DP[maxn],n,head,tail,que[maxn]; 8 9 struct land{ 10 long long x,y; 11 }arr[maxn],brr[maxn]; 12 13 bool cmp(const land &a,const land &b){ 14 if(a.x == b.x) return a.y < b.y; 15 else return a.x < b.x; 16 } 17 18 double conc(long long k,long long j){ 19 return (double)(DP[j]-DP[k])/(brr[k+1].y-brr[j+1].y); 20 } 21 22 int main(){ 23 scanf("%lld",&n); 24 25 for(int i = 1;i <= n;i++) 26 scanf("%lld%lld",&arr[i].x,&arr[i].y); 27 28 sort(arr+1,arr+1+n,cmp); 29 30 for(int i = 1;i <= n;i++){ 31 while(head && brr[head].y <= arr[i].y) head--; 32 brr[++head].x = arr[i].x; 33 brr[head].y = arr[i].y; 34 }n = head; 35 36 tail = 0,head = 0; 37 for(int i = 1;i <= n;i++){ 38 while(tail < head && conc(que[tail],que[tail+1]) < (double)brr[i].x) tail++; 39 DP[i] = DP[que[tail]]+brr[que[tail]+1].y*brr[i].x; 40 while(tail < head && conc(que[head],i) < conc(que[head-1],que[head])) head--; 41 que[++head] = i; 42 } 43 44 // for(int i = 1;i <= n;i++) printf("%d,%d\n",brr[i].x,brr[i].y); 45 46 47 printf("%lld",DP[n]); 48 49 return 0; 50 }