description:
地主想买一些长方形的土地,所有的土地可以分为若干组,每一组的土地的价格为这一组里的最长的长乘上最长的宽。土地的长和宽是不能交换的,例如一块2*5的土地和一块5*2的土地放在一起,价格为5*5=25。最少花费多少钱可以买下所有的土地。
Input:
第一行一个数n表示一共有n块土地。
接下来n行每行两个数xi和yi分别表示每块土地的长和宽。
Output:
一行一个数表示最小价格。
把每块地的一个顶点对齐坐标系原点,易知对于我们要考虑的每个点,都有(xi>xj || yi>yj) 如图
dp[i]表示前i块地最小价格;
可得dp方程 dp[n]=min(dp[j]+x[j+1]*y[n])j<n
可以推出,如果用k更新n比用j更新更优,有dp[k]-dp[j]<y[n]*(f[j]-f[k])
有决策单调性,我们用队列(?)来维护i 的最优更新。上代码(略丑)
#include<cstdio>
#include<algorithm>
using namespace std;
long long dp[50005];
struct sd{
long long x,y;
}d[50005],dd[50005];//存地
bool com(const sd &a,const sd &b)
{
if(a.x!=b.x)return a.x>b.x;
else return a.y>b.y;
}
long long cx[50005];
struct qj{
int num;
int l,r;
}que[50005];//存方案,l到r的dp用num来更新
int head=1,tail=0;
int pd(int l,int r,long long dq,long long fq)
{
if(l>r)return 0;
int mid=(l+r)/2;
if(l==r)return l;
if(cx[mid]*fq>dq&&cx[mid-1]*fq<=dq)return mid;
if(cx[mid]*fq>dq)return pd(l,mid-1,dq,fq);
else return pd(mid+1,r,dq,fq);
}//二分查找i更优的位置,即方案中的l
int tot;
long long f[50005];
void push(int x)
{
int k=que[tail].num;
while((f[k]-f[x])*dd[que[tail].l].y>dp[x]-dp[k]&&tail>=head)
{
tail--;
k=que[tail].num;
}//队尾方案的l用x更优,队尾方案被完全覆盖,出栈;
if((f[k]-f[x])*dd[tot].y>dp[x]-dp[k])//x这个方案能用来更新dp
{
int w;
if(tail<head)w=1;
else w=pd(que[tail].l,que[tail].r,dp[x]-dp[k],f[k]-f[x]);//找到x方案的l
que[tail].r=w-1;
tail++;
que[tail].l=w;
que[tail].r=tot;
que[tail].num=x;//x方案入队
}
}
int maxx(int x,int y)
{
return x>y?x:y;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%I64d%I64d",&d[i].x,&d[i].y);
d[n+1].x=0;
d[n+1].y=10000005;
sort(d+1,d+n+2,com);
int now=1;
for(int i=1;i<=n+1;i++)//处理出用来计算的点集
if(d[i].y>d[now].y)
{
dd[++tot].x=d[now].x;
dd[tot].y=d[now].y;
now=i;
}
for(int i=0;i<tot;i++)
f[i]=dd[i+1].x;
for(int i=1;i<=tot;i++)
cx[i]=dd[i].y;
que[++tail].l=1;
que[tail].r=tot;
que[tail].num=0;//方案0入队
for(int i=1;i<=tot;i++)
{
while(i>que[head].r)head++;//去掉队头过期元素
int k=que[head].num;
dp[i]=dp[k]+f[k]*dd[i].y;//更新i的dp
push(i);//把i方案入队
}
printf("%I64d",dp[tot]);
}