bzoj 1597 斜率DP
1597: [Usaco2008 Mar]土地購買
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 5115 Solved: 1897
[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
4100 1
15 15
20 5
1 100
輸入解釋:
共有4塊土地.
Sample Output
500HINT
FJ分3組買這些土地: 第一組:100x1, 第二組1x100, 第三組20x5 和 15x15 plot. 每組的價格分別為100,100,300, 總共500.
首先對決策的有序,對土地按照長 x,寬 y 遞增排序。
如果:
第一塊土地,和第二塊土地,第二塊土地長寬都要比第一塊大,那麽第一塊就等於不起作用,那麽可以不用考慮第一塊土地,
於是刪掉所有這種不需要考慮的土地,就成了 x 遞增,y 遞減排列的土地。
這時候,對於前面 i 塊土地來說,會可以分成很多部分,要成本最少的一種劃分。於是——DP思路就來了。
f[i] 前 i 塊土地的最優值。
那麽:
這樣O(n^2) 的算法就呼之欲出了,但是,還是會TLE;
怎麽辦呢?
斜率DP:
對於 i 點,與之相切的點 斜率才最小,保證 < x[i] ,這個點才是劃分點。
到這裏,分析就已經完成了,只差隊列維護決策點。這個凸多邊形了。
就是套路了,
- 考慮凸多邊相切的變化規律,找到劃分點。
- 用劃分點計算新的值。
- 新的值更新凸多邊形
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 50010; struct Node { ll x,y; bool operator < (const Node& rhs) const { if(x==rhs.x) return y < rhs.y; return x < rhs.x; } }p[maxn]; ll n,f[maxn],q[maxn]; double slope(long long a,long long b) { return (1.0*(f[a]-f[b])/(p[a+1].y-p[b+1].y)); } int main(int argc, char const *argv[]) { scanf("%I64d",&n); for(int i = 1; i <= n; i++) scanf("%I64d%I64d",&p[i].x,&p[i].y); sort(p+1,p+n+1); int cnt = 0; for(int i = 1; i <= n; i++) { if(p[i].y<=p[i+1].y) continue; while(cnt&&p[cnt].y<=p[i].y) --cnt; p[++cnt] = p[i]; } int h = 0,t = 1; q[h] = 0; for(int i = 1; i <=cnt; i++) { while(t-h>1&&slope(q[h],q[h+1])>=-p[i].x) ++h; //刪除隊首非最優決策點 f[i] = f[q[h]] + p[q[h]+1].y * p[i].x; while(t-h>1&&slope(q[t-2],q[t-1])<slope(q[t-1],i)) --t; q[t++] = i; } cout<<f[cnt]<<endl; return 0; }View Code
參考:http://www.cnblogs.com/akhpl/p/6715148.html
bzoj 1597 斜率DP