Land Acquisition[土地并购](USACO Mar08)
Task:
有n个长方形土地,已知各自的长与宽,并购两块土地的费用是两块土地长的较大值与较小值乘积,单块土地的费用即为长乘宽,求购置所有土地的费用。(n<=50000)
Solution:
第一道斜率优化题…
首先如果直接dp的话显然是无法转移的,所以先按长sort一波,然后宽比上一个小的就可以直接忽略不计了,因此宽是单调上升的。我们可以简单的写出一个朴素的dp方程:
dp[i]=dp[j]+Y[i]∗X[j+1]
这个方程显然是
O(n2)
的,无法通过,于是就有了斜率优化。
假设
j<k
,我们看看当前的dp[i]如果从k转移优于j,那么就会得到:
dp[k]+Y[i]∗X[k+1]<=dp[j]+Y[i]∗X[j+1]
可化为:
dp[k]−dp[j]<=Y[i]∗(X[j+1]−X[i+1])
即:
g(k,j)=dp[k]−dp[j]X[j+1]−X[i+1]<=Y[i]
因此,我们就知道了如果对于
j<k
,如果
g(k,j)<=Y[i]
,那么就可以丢弃
j
。显然为了保证决策最优,这个斜率的表达式是单调的。可以单调队列优化: - 首先判断
- 用当前最优解 Q[L] 来更新dp[i]
- 如果 g(i,Q[R])<=g(Q[R],Q[R−1]) 则丢弃 Q[R]
- 将i加入到队列中
于是我们就用 O(nlogn) 的复杂度完成了本题(dp复杂度 O(n) )
#include<stdio.h>
#include<iostream>
#include<algorithm>
#define ll long long
#define M 50005
using namespace std;
struct Node{
int x,y;
bool operator <(const Node &a)const{
return x>a.x;
}
}A[M],B[M];
ll dp[M];
int Q[M];
double g(int k,int j){//j<k
return 1.0*(dp[k]-dp[j])/(B[j+1].x-B[k+1].x);
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d %d",&A[i].x,&A[i].y);
sort(A+1,A+n+1);
int tot=0;
for(int i=1;i<=n;i++){
if(A[i].y<=B[tot].y)continue;
B[++tot]=A[i];
}
int L=0,R=-1;
Q[++R]=0;
for(int i=1;i<=tot;i++){
while(L<R&&g(Q[L+1],Q[L])<=B[i].y)L++;
int id=Q[L];
dp[i]=dp[id]+1LL*B[i].y*B[id+1].x;
while(L<R&&g(i,Q[R])<=g(Q[R],Q[R-1]))R--;
Q[++R]=i;
}
cout<<dp[tot]<<endl;
return 0;
}