• 洛谷 P2672 推销员


    题目描述

    阿明是一名推销员,他奉命到螺丝街推销他们公司的产品。螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住户。螺丝街一共有N家住户,第i家住户到入口的距离为Si米。由于同一栋房子里可以有多家住户,所以可能有多家住户与入口的距离相等。阿明会从入口进入,依次向螺丝街的X家住户推销产品,然后再原路走出去。

    阿明每走1米就会积累1点疲劳值,向第ii家住户推销产品会积累Ai点疲劳值。阿明是工作狂,他想知道,对于不同的X,在不走多余的路的前提下,他最多可以积累多少点疲劳值。

    思路分析

    一、为何不需要动归

    数据范围在十万,如果用动归肯定会爆时空。再说,题目要求输出多个值,从划分阶段的角度来讲就很难划分。既然是最优化问题,不来一发贪心怎能够抒发对动归的憎恨之情呢?

    二、怎么贪

    从样例可得,推销一家的疲劳值与与两个因素有关:距离Si,积累的疲劳值Ai

    具体一点,往返2Si,推销Ai。所以第一步就可以确定下来了:当X=1时,sum=max(2Si+Ai);

    接下来,Si与Ai仍然都对ans有关系。分类讨论如下:

    记走的最远距离为far

    ① 如果Si<far,那就可以顺路推销,sum+=A[i];

    ② 如果Si>far,多走(S[i]-far)*2,即sum+=(S[i]-far)*2;

    三、朴素实现

    #include<iostream>
    
    using namespace std;
    
    bool vis[100005];
    int a[100005],dis[100005],n,mx,mxdis,far=-1,tempa,tempb,sum;
    
    int before(int place)
    {
        int bmx=-1,bmxdis=-1;
        for(int i=1;i<=n;i++)
        {
            if(!vis[i])
            {
                if(a[i]>bmx)
                {
                    bmx=a[i];
                    bmxdis=i;
                }
            }
        }
        return bmxdis;
    }
    
    int after(int place)
    {
        //place+=1;
        int amx=-1,amxdis=-1;
        for(int i=place;i<=n;i++)
        {
            if(!vis[i])
            {
                if(a[i]+(dis[i]-place)*2>amx)
                {
                    amx=a[i]+(dis[i]-place)*2;
                    amxdis=i;
                }
            }
        }
        return amxdis;
    }
    
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>dis[i];
        }
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
        }
        for(int i=1;i<=n;i++)
        {
            if(a[i]+dis[i]*2>mx)
            {
                mx=a[i]+dis[i]*2;
                mxdis=i;
            }
        }
        cout<<mx<<endl;
        vis[mxdis]=1;
        far=mxdis;
        sum+=mx;
        for(int i=2;i<=n;i++)
        {
            tempb=before(far);
            tempa=after(far);
            if(a[tempb]>a[tempa]+(dis[tempa]-far)*2)
            {
                mxdis=tempb;
                mx=a[tempb];
            }
            else
            {
                mxdis=tempa;
                
                mx=a[tempa]+(dis[tempa]-far)*2;
                far=tempa;
            }
            vis[mxdis]=1;
            sum+=mx;
            cout<<sum<<endl;
        }
        return 0;
    }

    但是估计你也看到了,这种算法实在是太慢了,只能得60分(虽然在考场上已经足够了)。

    四、优化

    对于dalao来说,可以毫不费力地用上logn数据结构,如线段树和树状数组。可是我们这种蒟蒻,想都不要想。

    可以看到,每次都会选取far左边和右边能获得的最大疲劳值(计算方式不同)。既然是最大,那么可以使用优先队列(堆?)优化。

    但是优先队列不支持随机存取。如果far改变,就需要重新建立优先队列。

    #include<iostream>
    #include<cstdio>
    #include<queue>
    
    using namespace std;
    
    struct before_house{
        int num,distance,place;
    };
    
    struct after_house{
        int num,distance,place;
    };
    
    bool vis[100005];
    int a[100005],dis[100005],n,mx,mxdis,far=-1,tempa,tempb,sum;
    priority_queue<before_house> before;
    priority_queue<after_house> after;
    
    bool operator < (before_house h1,before_house h2)
    {
        return h1.num<h2.num;
    }
    
    bool operator < (after_house h1,after_house h2)
    {
        return h1.num+(h1.distance-far)*2<h2.num+(h2.distance-far)*2;
    }
    //两种优先队列的比较方式不同,需要进行重载
    void make_heap()
    {
        while(!before.empty())
        {
            before.pop();
        }
        while(!after.empty())
        {
            after.pop();
        }
        //清空
        before_house bh;
        after_house ah;
        for(int i=1;i<=far;i++)
        {
            if(!vis[i])
            {
                bh.num=a[i];
                bh.distance=dis[i];
                bh.place=i;
                before.push(bh);
            }
        }
        for(int i=far;i<=n;i++)
        {
            if(!vis[i])
            {
                ah.num=a[i];
                ah.distance=dis[i];
                ah.place=i;
                after.push(ah);
            }
        }
    }
    
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>dis[i];
        }
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
        }
        for(int i=1;i<=n;i++)
        {
            if(a[i]+dis[i]*2>mx)
            {
                mx=a[i]+dis[i]*2;
                mxdis=i;
            }
        }
        cout<<mx<<endl;
        vis[mxdis]=1;
        far=mxdis;
        sum+=mx;
        make_heap();
        for(int i=2;i<=n;i++)
        {
            if(!before.empty())
            {
            tempb=before.top().place;
        }
            if(!after.empty())
            {
            tempa=after.top().place;
        }
            if(a[tempb]>a[tempa]+(dis[tempa]-far)*2)
            {
                mxdis=tempb;
                mx=a[tempb];
                before.pop();
                vis[mxdis]=1;
            }
            else
            {
                mxdis=tempa;
                mx=a[tempa]+(dis[tempa]-far)*2;
                far=tempa;
                vis[mxdis]=1;
                make_heap();
            }
            
            sum+=mx;
            printf("%d
    ",sum);
        }
        return 0;
    }

    最后,祝大家NOIP2018    RP++

  • 相关阅读:
    FFmpeg简单使用:解封装 ---- 基本流程
    SDL播放PCM音频数据
    JDK8时间新API-2
    RocketMq延时队列的实现原理
    Kibana复杂查询语句
    Es基础api
    Redis sscan命令
    如何实现分布式的延时队列
    客户端从broker拉取的messagequeue的样子
    RocketMq多个consumerQueue长什么样子
  • 原文地址:https://www.cnblogs.com/ehznehc/p/9919619.html
Copyright © 2020-2023  润新知