题意
给出n块土地,每块土地都有其长和宽。若把若干块土地放在一起购买则需要的花费为最大的长*最大的宽。求购买所有土地的最小花费。
n<=50000
分析
首先很容易得到,若有两块土地长宽分别为l1,w1,l2,w2,若满足l1>=l2且w1>=w2则第二块土地可以忽略不计。
那么就先把所有没用的土地都去掉,并按长从小到大排序,那么宽自然就是递减的了。显然每次必然选取连续的一段,设
f[i]
表示前i块分成若干段的最小花费,显然可以得到
f[i]=min(f[j]+w[j+1]∗l[i])
我们设有j < k且对于i而言选j比选k更优,那么必然有 f[j]+w[j+1]∗l[i]<f[k]+w[k+1]∗l[i]
化简得 f[j]−f[k]b[k+1]−b[j+1]>a[i]
那么问题就转换成了典型的斜率优化dp了,直接上板子即可。
移项的时候注意符号。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define N 50005
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
int n,q[N];
ll f[N];
struct data{ll l,w;}a[N];
bool cmp(data a,data b)
{
return a.l<b.l||a.l==b.l&&a.w<b.w;
}
ll get1(int j,int k)
{
return f[j]-f[k];
}
ll get2(int j,int k)
{
return a[k+1].w-a[j+1].w;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%lld%lld",&a[i].l,&a[i].w);
sort(a+1,a+n+1,cmp);
ll mx=0;int n1=n;
for (int i=n;i>=1;i--)
{
if (a[i].w<=mx)
{
a[i].l=inf;
n1--;
}
else mx=a[i].w;
}
sort(a+1,a+n+1,cmp);
n=n1;
int head=1,tail=1;
q[1]=0;
for (int i=1;i<=n;i++)
{
while (head<tail&&get1(q[head],q[head+1])>=get2(q[head],q[head+1])*a[i].l) head++;
f[i]=f[q[head]]+(ll)a[i].l*a[q[head]+1].w;
while (head<tail&&get1(q[tail-1],q[tail])*get2(q[tail],i)>get1(q[tail],i)*get2(q[tail-1],q[tail])) tail--;
q[++tail]=i;
}
printf("%lld",f[n]);
return 0;
}