洛谷 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\n",sum);
    }
    return 0;
}

最后,祝大家NOIP2018    RP++

posted @ 2018-11-06 23:18  _wkjzyc  阅读(138)  评论(0编辑  收藏  举报