BZOJ1597: [Usaco2008 Mar]土地购买

【传送门:BZOJ1597


简要题意:

  给出n块土地,给出每块土地的长和宽,可以将n块土地分成若干组,每一组的费用是组中的长最大的土地的长与宽最大的土地的宽的乘积,求出将n块分成若干组的最小费用


题解:

  首先我们将一些土地排除,排除哪些土地呢?

  先将土地按长度递增排序,然后长度相同按宽度递增排序

  然后排除一些长与宽都能被任意土地包含的土地,把这些土地去掉,并不影响答案

  首先想到DP,f[i]表示将i块土地分组后的最小值,因为我们排序了,而且排除了一些重合的情况后,得到的土地排序肯定是长度递增而且宽度递减的,所以很容易得到DP方程f[i]=min(f[j]+s[i].x*s[j+1].y),s结构体记录土地的长和宽(x为长,y为宽),但是时间复杂度O(n2),会超时

  那么就用斜率优化来优化


参考代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
using namespace std;
typedef long long LL;
struct node
{
    LL x,y;
}a[51000],s[51000];
int cmp(const void *xx,const void *yy)
{
    node n1=*(node *)xx;
    node n2=*(node *)yy;
    if(n1.x<n2.x) return -1;
    if(n1.x>n2.x) return 1;
    if(n1.y<n2.y) return -1;
    if(n1.y>n2.y) return 1;
    return 0;
}
/*
f[i]=min(f[j]+s[i].x*s[j+1].y)
j1<j2<i
f[j2]+s[i].x*s[j2+1].y<=f[j1]+s[i].x*s[j1+1].y
(f[j2]-f[j1])/(s[j1+1].y-s[j2+1].y)<=s[i].x
*/
LL f[51000];
double slop(int j1,int j2)
{
    return (f[j2]-f[j1])/(s[j1+1].y-s[j2+1].y);
}
int list[51000];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lld%lld",&a[i].x,&a[i].y);
    qsort(a+1,n,sizeof(node),cmp);
    int len=0;
    s[1]=a[1];len=1;
    for(int i=2;i<=n;i++)
    {
        while(len>0&&a[i].y>=s[len].y) len--;
        s[++len]=a[i];
    }
    memset(f,0,sizeof(f));
    int head=1,tail=1;list[1]=0;
    for(int i=1;i<=len;i++)
    {
        while(head<tail&&slop(list[head],list[head+1])<=double(s[i].x)) head++;
        int j=list[head];
        f[i]=f[j]+s[i].x*s[j+1].y;
        while(head<tail&&slop(list[tail-1],list[tail])>slop(list[tail],i)) tail--;
        list[++tail]=i;
    }
    printf("%lld\n",f[len]);
    return 0;
} 
posted @ 2017-10-06 11:01  Star_Feel  阅读(198)  评论(0编辑  收藏  举报