[USACO 2008 March Gold] 土地购买

版权声明:全文无版权,目前博客已搬迁至https://bill.moe https://blog.csdn.net/Bill_Yang_2016/article/details/54864284

题目描述

  农夫John准备扩大他的农场,他正在考虑N (1 <= N <= 50,000) 块长方形的土地. 每块土地的长宽满足(1 <= 宽 <= 1,000,000; 1 <= 长 <= 1,000,000).
  每块土地的价格是它的面积,但FJ可以同时购买多快土地. 这些土地的价格是它们最大的长乘以它们最大的宽, 但是土地的长宽不能交换. 如果FJ买一块3x5的地和一块5x3的地,则他需要付5x5=25.
  FJ希望买下所有的土地,但是他发现分组来买这些土地可以节省经费. 他需要你帮助他找到最小的经费.


输入格式

  • 第1行: 一个数: N
  • 第2..N+1行: 第i+1行包含两个数,分别为第i块土地的长和宽

输出格式

  • 第一行: 最小的可行费用.

样例数据

样例输入

4
100 1
15 15
20 5
1 100

样例输出

500

样例解释

输入解释:共有4块土地.
输出解释:FJ分3组买这些土地: 第一组:100x1, 第二组1x100, 第三组20x5 和 15x15 plot. 每组的价格分别为100,100,300, 总共500.


题目分析

题目拿在手里毫无思路,于是浏览了一下黄学长的题解,没怎么看懂,只看懂了一句话:
妈蛋被各种题解坑了半天不如自己手推靠谱
好吧,那就自己推吧。
将土地按照x从小到大排序
假设f[i]表示前i块土地的最小价格
f[i]=f[j]+x[i]*y[i]  j∈(1,i-1)
卧槽这方程怎么只与i有关,肯定是错的,哪里错了呢?
错在,这个方程相当于选了最大的x与最大的y,样例100*100=10000,显然错误

考虑一下怎么改写方程,我们需要一个较大的x,较大的y作为分组,如果遇到如下数据:
1 1
2 2
3 3
那么这三个都可以直接分在一组用3*3肯定是最优的,那么1 2都可以直接去掉。

处理完以上内容后,就会发现,土地的x依次递增,土地的y依次递减,那么动规方程就可以轻松写出来了:

fi=fj+xiyj+1

接着斜率优化维护下凸包即可
注意因为方程化简后是y=-kx+b
故需要将负号收到x中间去
那么计算斜率时的x顺序要反一下


源代码

#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
typedef long long LL;
inline const int Get_Int() {
    LL num=0,bj=1;
    char x=getchar();
    while(x<'0'||x>'9') {
        if(x=='-')bj=-1;
        x=getchar();
    }
    while(x>='0'&&x<='9') {
        num=num*10+x-'0';
        x=getchar();
    }
    return num*bj;
}
struct Land {
    LL x,y;
    bool operator < (const Land& b) const {
        return (x==b.x&&y<b.y)||(x<b.x);
    }
} a[50005],b[50005];
LL n,cnt=0,f[50005],Q[50005];
double Slope(LL j,LL k) {
    return (double)(f[j]-f[k])/(b[k+1].y-b[j+1].y);
}
int main() {
    n=Get_Int();
    for(int i=1; i<=n; i++) {
        a[i].x=Get_Int();
        a[i].y=Get_Int();
    }
    sort(a+1,a+n+1);
    for(int i=1; i<=n; i++) { //去除无用土地 
        while(cnt>0&&a[i].y>=b[cnt].y)cnt--;
        b[++cnt]=a[i];
    }
    int Left=1,Right=1;
    Q[1]=0;
    for(int i=1; i<=cnt; i++) {
        while(Left<Right&&Slope(Q[Left],Q[Left+1])<=b[i].x)Left++; //维护队首(删除非最优决策)
        int Front=Q[Left];
        f[i]=f[Front]+b[i].x*b[Front+1].y;
        while(Left<Right&&Slope(Q[Right-1],Q[Right])>=Slope(Q[Right],i))Right--; //维护队尾(维护下凸包性质)
        Q[++Right]=i;
    }
    printf("%lld\n",f[cnt]);
    return 0;
}

展开阅读全文

没有更多推荐了,返回首页