bzoj1597 土地购买

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]);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值