洛谷 P2900 [USACO08MAR]Land Acquisition G(斜率优化dp)
题目链接:P2900 [USACO08MAR]Land Acquisition G
终于考完试了,断更了一个月去准备期末去了,今天开始继续更新。
题解:
非常经典的斜率优化
d
p
dp
dp,今天刚学,写篇题解记录下
首先我们可以想到,如果某块地的长宽都比某一块地小,那么一定是把这块地合并购买,此时就相当于不用考虑这块地了,因此我们可以先把所以地按长宽排序后把可以合并的忽略,只留下需要考虑的地,把所有地按
w
w
w降序,
w
w
w相同时按
l
l
l降序,这样预处理万后的地一定是按
w
w
w递减,
l
l
l递增
此时我们用 d p [ i ] dp[i] dp[i]表示前i块土地的最小花费,考虑 d p dp dp,可以推出状态转移方程:
dp[i]=min(dp[j]+w[j+1]*l[i])(j<i)
时间复杂度是
O
(
n
2
)
O(n^2)
O(n2),显然不能接受,观察转移方程发现是可以斜率优化的,考虑
0
<
j
<
k
<
i
0<j<k<i
0<j<k<i
如果
k
k
k比
j
j
j好,那么满足以下式子:
dp[j]+w[j+1]*l[i]>dp[k]+w[k+1]*l[i]
=>l[i]*(w[j+1]-w[k+1])>dp[k]-dp[j]
因为w是递减的,即: w [ j + 1 ] − w [ k + 1 ] > 0 ( j < k ) w[j+1]-w[k+1]>0(j<k) w[j+1]−w[k+1]>0(j<k)
=>l[i]>(dp[k]-dp[j])/(w[j+1]-w[k+1])
此时我们用单调队列维护即可,具体和细节参考代码
struct elemt{
ll w,l;
bool operator<(elemt v)const{//按w从高到低,w相同时l从高到低排序
if(w==v.w){
return l>v.l;
}
return w>v.w;
}
};
elemt a[N];//土地块
ll dp[N];//前i块土地的最小花费
long double get_k(int x,int y){//获取斜率
return 1.0*(dp[y]-dp[x])/(a[x+1].w-a[y+1].w);
}
int q[N];//单调队列
int main(){
/*cout<<setiosflags(ios::fixed)<<setprecision(8)<<ans<<endl;//输出ans(float)格式控制为8位小数(不含整数部分)*/
/*cout<<setprecision(8)<<ans<<endl;//输出ans(float)格式控制为8位小数(含整数部分)*/
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);//同步流
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].w>>a[i].l;
}
sort(a+1,a+n+1);
int cnt=1;//合并后土地块数量
for(int i=2;i<=n;i++){//只留下不能被合并的部分,a处理后一定满足w递减,l递增
if(a[cnt].l<a[i].l){
a[++cnt]=a[i];
}
}
n=cnt;
int l=1,r=1;//单调队列下标
q[1]=0;//初始点
for(int i=1;i<=n;i++){
while(l<r&&get_k(q[l],q[l+1])<=a[i].l){//满足l[i]>(dp[k]-dp[j])/(w[j+1]-w[k+1])的条件,此时q[l]不如q[l+1]优,踢出q[l]
l++;
}
dp[i]=dp[q[l]]+a[q[l]+1].w*a[i].l;
while(l<r&&get_k(q[r-1],q[r])>=get_k(q[r],i)){//保证斜率是递增的,
//如果当前斜率小于上一个斜率,那么上一个不合法当前一定不合法(l递增),上一个如果合法也轮不到当前,那么当前就没有存在的必要
r--;
}
q[++r]=i;
}
cout<<dp[n]<<endl;
return 0;
}