• BZOJ 1597 Usaco2008 Mar土地购买


    1597: [Usaco2008 Mar]土地购买

    Time Limit: 10 Sec  Memory Limit: 162 MB
    Submit: 5515  Solved: 2068
    [Submit][Status][Discuss]

    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

    这道题网上的题解让人看得晕乎乎的,这里就来大力写一发

    首先这道题肯定是dp无疑

    我们发现有很多的状态我们是不需要的

    比如当x[i]<x[j]&&y[i]<y[j]的时候,那么i就可以作为j的附属品

    只有可能j参与计算而i一定不会参与计算

    那么我们就可以把这些状态去掉

    变量声明如下

    int n;ll f[MAXN];//n为元素个数,f[i]表示枚举到i的时候最小花费 
    struct node{
    	ll x,y;//x为长,y为宽 
    }e[MAXN],ans[MAXN];//e[i]代表原数组,ans[i]代表去重后的数组

    内部排序方式如下

    inline bool mycmp(node n,node m){
        return n.x==m.x?n.y<m.y:n.x<m.x;
    }

    代码如下

    sort(e+1,e+n+1,mycmp);
    int tmp=-1000;
    for(int i=n;i>=1;i--){
        if(e[i].y>tmp) tmp=e[i].y;
        else vis[i]=1;
    }
    int tot=0;
    for(int i=1;i<=n;i++){
        if(!vis[i]) ans[++tot].x=e[i].x,ans[tot].y=e[i].y;
    }

    把ans数组也按上述排序方式进行排序

    我们发现ans.y是严格单调递减的  为什么呢?

    排序后ans.x单调递增,如果出现ans[i-1].y<ans[i].y的话那么i-1就会成为i的附属品

    接下来就很显然了,如果从i到j分为一组的话,面积为ans[i].y*ans[j].x

    那么就可以进行dp

    直接dp代码如下

    for(int i=1;i<=n;i++){
       for(int j=1;j<i;j++){
           f[i]=min(f[i],f[j]+e[j+1].y*e[i].x);
       } 
    } 

    我们发现这样的时间复杂度是n^2的

    我们考虑如何优化

    我们发现如果存在k比q有的话

    有f[k]+ans[k+1].y*ans[i].x<f[q]+ans[q+1].y*ans[i].x

    变形可得

    (f[k]-f[q])/(ans[q+1].y-ans[k+1].y)<ans[i].x

    所以只要满足这个式子k就比q要优

    接下来就比较简单了 维护一个单调递增的队列,队列里存元素的下标

    枚举到i的时候如果对头head+1和head满足上面的那个式子的话,就说明对头head没有head+1优,就可以把对头出队,让head+1来当对头,一直进行到不满足上面的那个式子为止

    这时候的队头就是最优的,那么f[i]=f[head]+ans[head+1].y*ans[i].x

    把计算出来的f[i]拿去用上述的式子去把队尾那些不优的元素去掉,可怎样才算不优的呢

    如果i比队尾tail优,队尾tail比tail-1优那么tail的存在就没有任何意义

    那么dp转移代码就可以写成如下

    head=tail=0;
    for(int i=1;i<=tot;i++){
        while(head<tail&&getlow(q[head],q[head+1])<ans[i].x) head++;
        int t=q[head];
        f[i]=f[t]+1LL*ans[t+1].y*ans[i].x;
        while(head<tail&&getlow(q[tail],i<getlow(q[tail1],q[tail])) tail--;
        q[++tail]=i;
    }

    最后输出f[tot]就可以了

    完整的代码

    #include <bits/stdc++.h>
    #define ll long long 
    using namespace std;
    inline int read(){
        int x=0;int f=1;char ch=getchar();
        while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    const int MAXN=1e6+10;
    int n;ll f[MAXN];//n为元素个数,f[i]表示枚举到i的时候最小花费
    struct node{
        ll x,y;//x为长,y为宽 
    }e[MAXN],ans[MAXN];//e[i]代表原数组,ans[i]代表去重后的数组 
    namespace zhangenming{
        inline bool mycmp(node n,node m){
            return n.x==m.x?n.y<m.y:n.x<m.x;
        }
        inline double getlow(int a,int b){
            return (f[a]-f[b])/(ans[b+1].y-ans[a+1].y);
        }
        ll vis[MAXN]={},q[MAXN],head,tail;
        void init(){
            n=read();
            for(int i=1;i<=n;i++){
               e[i].x=read();
               e[i].y=read();
            }
            sort(e+1,e+n+1,mycmp);
            int tmp=-1000;
            for(int i=n;i>=1;i--){
                if(e[i].y>tmp) tmp=e[i].y;
                else vis[i]=1;
            }
            int tot=0;
            for(int i=1;i<=n;i++){
                if(!vis[i]) ans[++tot].x=e[i].x,ans[tot].y=e[i].y;
            }
            head=tail=0;
            for(int i=1;i<=tot;i++){
                while(head<tail&&getlow(q[head],q[head+1])<ans[i].x) head++;
                int t=q[head];
                f[i]=f[t]+1LL*ans[t+1].y*ans[i].x;
                while(head<tail&&getlow(q[tail],i)<getlow(q[tail-1],q[tail])) tail--;
                q[++tail]=i;
            }cout<<f[tot]<<endl;
        }
    }
    int main(){
        //freopen("a.in","r",stdin);
        //freopen("a.out","w",stdout);
        using namespace zhangenming;
        init();
        return 0;
    }
    

      

  • 相关阅读:
    文件上传.py
    文件下载.py
    Visual SVN IIS反向代理设置
    开发者工具
    Linux
    装饰器
    面向对象
    django异常--数据库同步
    浅谈 Mysql
    python网络编程系列
  • 原文地址:https://www.cnblogs.com/something-for-nothing/p/7788759.html
Copyright © 2020-2023  润新知