• [USACO]Land Acquisition G「斜率优化DP」


    [USACO]Land Acquisition G「斜率优化DP」

    题目描述

    Farmer John 准备扩大他的农场,眼前他正在考虑购买 (N) 块长方形的土地。

    如果 FJ 单买一块土地,价格就是土地的面积。但他可以选择并购一组土地,并购的价格为这些土地中最大的长乘以最大的宽。比如 FJ 并购一块 (3×5) 和一块 (5×3) 的土地,他只需要支付 (5×5=25) 元, 比单买合算。

    FJ 希望买下所有的土地。他发现,将这些土地分成不同的小组来并购可以节省经费。 给定每份土地的尺寸,请你帮助他计算购买所有土地所需的最小费用。

    输入格式

    第一行一个整数 (N)(1≤N≤5×10^4))。

    接下来 (N) 行,每行两个整数 (w_i)(l_i),代表第 (i) 块土地的长和宽。保证土地的长和宽不超过 (10^6)

    输出格式

    输出买下所有土地的最小费用。

    输入输出样例

    输入 #1

    4 
    100 1 
    15 15 
    20 5 
    1 100 
    

    输出 #1

    500 
    

    思路分析

    • 显然如果直接按原序列下手会很棘手,所以我们首先要将序列排好序
      • 为了方便合并,所以我们以长为关键字进行排序,这时候好像还是有点无从下手,所以尝试找一些性质
      • 不难想到,如果一块土地的长和宽均小于某一块土地,那这块土地其实是不会对答案产生任何影响的,所以我们在排好序以后可以将其去除,而只留下合并时会改变长宽的
      • 在进行完的排序和去除操作以后,这时候的序列一定满足这样的性质:长是递减的,而宽是递增的(自己模拟一下就好)
    • 那么此时,对于一段区间,我们只需要取两端即可,因为一个长最大,一个宽最大,区间内的自然就一起合并了
      • 由此得出转移方程: (f[i] = min(f[i],f[j-1]+x_j*y_i)) ((0<j<i))((x) 为长,(y) 为宽)
      • 变一下型就是 (f[j-1]=f[i]-y_i*x_j),这样就是一个很简单的可以进行斜率优化的式子了,斜率为 (y_i)

    Code

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define N 50010
    using namespace std;
    inline int read(){
    	int x = 0,f = 1;
    	char ch = getchar();
    	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	return x*f;
    }
    int n,q[N];
    long long f[N];
    struct farm{
    	int x,y;
    }a[N];
    inline bool cmp(farm a,farm b){
    	return a.x==b.x ? a.y>b.y : a.x>b.x;
    }
    double slope(int x,int y){
    	return (double)(f[x]-f[y])/(double)(a[y+1].x-a[x+1].x);
    }
    int main(){
    	n = read();
    	for(int i = 1;i <= n;i++){
    		a[i].x = read(),a[i].y = read();
    	}
    	sort(a+1,a+1+n,cmp);
    	int tot = 0;
    	for(int i = 1;i <= n;i++)if(a[i].y>a[tot].y)a[++tot] = a[i]; //去除操作
    	n = tot;
    	int head = 0,tail = 0;
    	for(int i = 1;i <= n;i++){ //按转移方程直接上斜率优化即可
    		while(head<tail&&slope(q[head],q[head+1])<=a[i].y)head++;
    		f[i] = f[q[head]]+1ll*a[q[head]+1].x*a[i].y;
    		while(head<tail&&slope(q[tail-1],q[tail])>=slope(q[tail],i))tail--;
    		q[++tail] = i;
    	}
    	printf("%lld",f[n]);
    	return 0;
    }
    
  • 相关阅读:
    安装SQL sever2008时显示重新启动计算机规则失败,应该怎么解决?
    C#获取当前日期时间(转)
    使用JQUERY实现页面局部刷新
    Metal渲染:实现画面比例功能
    Metal渲染:实现旋转/翻转功能
    依赖注入浅析
    iOS 消息推送实现 APNS
    使用#pragma阻止一些warnings
    github 多帐户使用
    Swift 实现Bitmask Option(Enum)
  • 原文地址:https://www.cnblogs.com/hhhhalo/p/13512988.html
Copyright © 2020-2023  润新知