bzoj-1597 [Usaco2008 Mar]土地购买

1597 [Usaco2008 Mar]土地购买
题目链接
Time Limit 10 Sec Memory Limit 162 MB
Submit 6013 Solved 2278
Description
农夫John准备扩大他的农场,他正在考虑N (1 = N = 50,000) 块长方形的土地. 每块土地的长宽满足(1 = 宽
= 1,000,000; 1 = 长 = 1,000,000). 每块土地的价格是它的面积,但FJ可以同时购买多快土地. 这些土地的价
格是它们最大的长乘以它们最大的宽, 但是土地的长宽不能交换. 如果FJ买一块3x5的地和一块5x3的地,则他需要
付5x5=25. FJ希望买下所有的土地,但是他发现分组来买这些土地可以节省经费. 他需要你帮助他找到最小的经费.
Input
第1行 一个数 N
第2..N+1行 第i+1行包含两个数,分别为第i块土地的长和宽
Output
第一行 最小的可行费用.

Sample Input
4

100 1

15 15

20 5

1 100

共有4块土地.
Sample Output
500

FJ分3组买这些土地

第一组100x1,

第二组1x100,

第三组20x5 和 15x15 plot.

每组的价格分别为100,100,300, 总共500.
HINT
Source
Gold

题解
斜率优化,这篇写得相对简单一些,如果初学可以看看luogu-P2120 [ZJOI2007]仓库建设
zzk大佬的博客,这篇写得很详细,我也是看这篇学会的。

首先我们有一个贪心的想法:如果一个土地 i 的长和宽(x[i] 和 y[i])都小于另一块土地 j (x[i] 和 y[j]),那么这块土地肯定不用单独购买(也就是免费了)。
排序合并后(x 为第一关键字,y 第二,全部递减顺序),x[i] 是递增的,y[i]是递减的(想一想为什么?)
所以 f [ i ] = m i n { f [ j ] + x [ j + 1 ] y [ i ] }
设选 j 为前一状态比 k 好其中 j > k。可得 f [ j ] + x [ j + 1 ] y [ i ] < f [ k ] + x [ k + 1 ] y [ i ]
移项得斜率式: ( f [ j ] f [ k ] ) / ( x [ k + 1 ] x [ j + 1 ] ) < y [ i ]

我们设 g ( j , k ) = ( f [ j ] f [ k ] ) / ( x [ k + 1 ] x [ j + 1 ] ) < y [ i ]
分类讨论一下能得到当 g ( i , j ) <= g ( j , k ) 时 j 没意义(其中 i>j>k )。
讨论的时候注意一下 g ( i , j ) = g ( j , i ) ,顺序不影响这个值,我自己经常会认为 g ( i , j ) = g ( j , i ) ,希望大家不要和我犯同样的错误了QWQ。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
#define DB double
using namespace std;
const int maxn=5e4+5;
int n,m,q[maxn],hea,til;
LL f[maxn];
struct js{
    LL x,y;
    bool operator <(const js &b)const{return x>b.x||(x==b.x&&y>b.y);}
}a[maxn];
DB xie(int i,int j){return (f[i]-f[j])*1.0/(a[j+1].x-a[i+1].x);}
int read()
{
    int ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}
int main()
{
    n=read();
    for (int i=1;i<=n;i++) a[i]=(js){read(),read()};
    sort(a+1,a+n+1);
    for (int i=1,lst=0;i<=n;i++)
      if (a[lst].y<a[i].y) a[++m]=a[i],lst=i;
    for (int i=1;i<=m;i++)
    {
        while (hea<til&&xie(q[hea+1],q[hea])<a[i].y) hea++;
        f[i]=f[q[hea]]+(LL)a[q[hea]+1].x*a[i].y;
        while (hea<til&&xie(q[til],i)<=xie(q[til-1],q[til])) til--;
        q[++til]=i;
    }
    printf("%lld",f[m]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xu0_zy/article/details/80458887