zoukankan      html  css  js  c++  java
  • bzoj1597 [Usaco2008 Mar]土地购买

    [Usaco2008 Mar]土地购买

    Time Limit: 10 Sec Memory Limit: 162 MB

    Description

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

    Input

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

    Output

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

    Sample Input

    4
    100 1
    15 15
    20 5
    1 100

    Sample Output

    500

    输入解释:

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

    显然有些田是白送的2333.。。。。(只要存在有别的田比这块田的长和宽都大,那么这块田直接白送。。。。。对吧)
    然后把这些直接去掉。。。。
    由于事先已经知道是斜率优化。。。(提示太明显了。。感觉刷专题就这一点不怎么好。。。)
    我们就去凑那个式子
    先按x从小到大排序。。。因为已经去掉了不用的土地,那么此时的y一定就是从大到小排的序
    (dp[i])表示买前(i)块土地的最优解,所以有了状态转移方程方程:
    (dp[i] = dp[j] + x[i] * y[j+1])
    写成一次函数的形式:
    (-dp[j]=y[j+1]*x[i]-dp[i])
    所以每一个点就是((y[j+1],-dp[j]))
    (k=x[i])满足单调性
    当然这里就差不多了,但是由于上一篇我说的不是很清楚,我想再多说一句。。。
    最开始排除的是两个点的斜率小于(k)的,其实是因为画图可以看出后面的点答案一定优于前面的。而当斜率大于k时,虽然当前的答案时前面的点更好(所以计算答案的时候就用的是第一个满足条件的点),但是由于(k)在单增,有可能(k)会超过它的斜率,所以不能确定。
    而倒着删是因为如果不是个下凸包形状的话,那么中间那个点一定不是最优点,要么它前面的那个点是最优的,要么后面的是最优的。

    
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 5e4 + 5;
    struct lpl{
    	long long x, y;
    }lin, ini[maxn], field[maxn], point[maxn];
    int n, tot, cnt, head, tail;
    long long dp[maxn];
    
    inline bool cmp(lpl a, lpl b)
    {
    	return a.x == b.x ? a.y < b.y : a.x < b.x;
    }
    
    inline void putit()
    {
    	scanf("%d", &n);
    	for(int i = 1; i <= n; ++i) scanf("%lld%lld", &ini[i].x, &ini[i].y);
    }
    
    inline double slop(lpl a, lpl b)
    {
    	return (double)(a.y - b.y) / (a.x - b.x);
    }
    
    inline void workk()
    {
    	sort(ini + 1, ini + n + 1, cmp);
        for(int i = 1; i <= n; i++){
            while(tot && ini[i].y >= field[tot].y) tot--;
            field[++tot] = ini[i];
        }
        point[head].x = field[1].y; point[head].y = 0;
        for(int i = 1; i <= tot; ++i){
        	while(head < tail && slop(point[head], point[head + 1]) < field[i].x)	head++;
        	dp[i] = (-point[head].y) + field[i].x * point[head].x;
    		lin.x = field[i + 1].y, lin.y = (-dp[i]);
        	while(head < tail && slop(point[tail], point[tail - 1]) > slop(point[tail], lin)) tail--;
        	point[++tail] = lin;
        }
        cout << dp[tot];
    }
    
    int main()
    {
    	putit();
    	workk();
    	return 0;
    }
    
    
    心如花木,向阳而生。
  • 相关阅读:
    Vim常用命令
    [转载] Java注解
    学习Zookeeper之第3章Zookeeper内部原理
    学习Zookeeper之第2章Zookeeper安装
    学习Zookeeper之第1章Zookeeper入门
    《Effective Java 2nd》第8章 通用程序设计
    《Effective Java 2nd》第7章 方法
    Log4j2报错ERROR StatusLogger Unrecognized format specifier
    比较三个数,求最大数字 ( 应用条件运算符:?)
    两个数字比较大小的方法 (分别应用if-else和条件运算符实现)
  • 原文地址:https://www.cnblogs.com/LLppdd/p/8661139.html
Copyright © 2011-2022 走看看