noip 2015 pj 普及组 第四题 推销员 salesman

这道题的题目大意就是说一个人要到一段死胡同去推销,拜访第i个门户会消耗a[i]体力,走路也会消耗一些体力,第i个门户距离胡同口的距离是s[i],然后让你输出在不多余走路的情况下拜访1户(x==1),拜访2户(x==2)......拜访n(x==n)户分别最多要消耗多少体力。

初看这道题自己感觉没有任何头绪,于是反复读样例,发现这道题有一定的规律,当x==1时,答案就是最大的s[i]*2+a[i],这个值可以在输完s[i]的时候就判断出来。其中s[i]*2的原因是在死胡同里面走的时候,如果你要走到s[i]的深度且不走多余的路,那么从进去到出来你一定会走2*s[i]的路程,对吧?

那么此时我们已经有了x==1的答案,那么可以猜测我们是不是可以通过x==1来推出x==2,x==3乃至x==n的答案来呢?

首先,可以暴力枚举......不过一看数据规模,n<=100000,简直了,还是算了吧......

然后,继续反复读样例说明,每次x的值增加1的时候,一定会多走一个门户(其实这是一句废话),那么根据这条性质,我们需要多走的那个门户一定就是当前的未走的门户中耗费体力值最大的。ans[i]=ans[i-1]+max(门户消耗的体力),一定是这样的吧?那么,我们是不是可以每当x循环增加1以后,都找出当前还没走的门户消耗体力的最大值,也就是找出max(门户消耗的体力)......嗯,一定是这样......

可是每次这样找最大值就成了O(n^2)的复杂度,明显还是爆......那么该怎么办呢?显然,每次找最大值会进行大量的重复计算,所以我们可以使用优先队列,也就是建立一个大根堆,这样每次只用调用堆顶元素就可以找到当前未走的门户中耗费体力值最大的了。

既然使用优先队列,可以直接用STL里边的模板。

这道题貌似就解决了?NoNoNo!并没有这么简单!你看,我们的情况实际上有两种。一种情况是当前门户比之前最深的门户更深,此时我们需要考虑一下多走的路程所消耗的体力,还有一种情况是当前门户比之前最深的浅一些,这种情况比较简单,关键是第一种情况该如何处理。那么请放开你的脑洞,我们是不是可以建立两个堆,分别储存两种情况的最大值呢?

这就比较复杂了,先建立两个结构体堆,分别命名为lheap和rheap,分别存储较浅的门户和更深的门户,每一个节点分别储存的是

struct rec
{
    int data;//拜访当前户用的体力值 
    int l;    //当前户与路口的距离 
    int sum;//拜访当前户的总体力值 (lheap存的是当前点的访问花费 rheap存的是单独访问当前点的花费) 
    int xb;//数组下标 
};

lheap的sum中由于不用考虑路程(总路程不会改变),所以光存当前门户的拜访花费即可;而rheap则要考虑路程的变化,为了方便计算,直接存储单独访问当前门户的花费,也就是s[i]*2+a[i]的值

既然考虑要判断当前的总路程,我们可以用一个l变量来记录

然后我们可以建立一个ans数组,存储x==1~n的答案,每次比较lheap和rheap的堆顶元素,将总消耗量大的加入到ans[i]中,就可以了

不过还有一个细节要处理,如果当前较大的堆顶元素是rheap中的,那我们还要刷新一下l的值,同时将rheap中深度小于新的l的值放进左堆中,代表它们的深度已经小于当前的最大门户深度了,这可以用一个while来实现

总时间复杂度为O(nlog(n)),不会TLE

于是我写了一个程序,放到洛谷上评测,结果就AC了

代码如下

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
int l;
int ans[100005];
struct rec
{
    int data;//拜访当前户用的体力值 
    int l;    //当前户与路口的距离 
    int sum;//拜访当前户的总体力值 (lheap存的是当前点的访问话费 rheap存的是单独访问当前点的花费) 
    int xb;//数组下标 
};

bool operator < (const rec &x,const rec &y)
{
    return x.sum < y.sum;
}
priority_queue<rec> lheap;
priority_queue<rec> rheap;
int n;
int s[100005];
int a[100005]; 
rec maxx;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&s[i]);//读入路程 
    }
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&a[i]);//读入拜访花费 
        if(2*s[i]+a[i]>=maxx.sum)//找出一开始总花费最多的那一门户,并用maxx记录下来 
        {
            maxx.sum=2*s[i]+a[i];
            maxx.l=s[i];
            maxx.xb=i;
            maxx.data=a[i];
        }
    }
    printf("%d\n",maxx.sum);//先将x==1的情况输出 
    rec ls;
    for(int i=1;i<maxx.xb;++i)//建立左堆 
    {
        ls.data=a[i];
        ls.l=s[i];
        ls.xb=i;
        ls.sum=a[i];
        lheap.push(ls);
    }
    for(int i=maxx.xb+1;i<=n;++i)//建立右堆 
    {
        ls.data=a[i];
        ls.l=s[i];
        ls.xb=i;
        ls.sum=2*s[i]+a[i];
        rheap.push(ls);
    }
    int lx;
    l=maxx.l*2;//记录当前的最远距离 
    rec lsl,lsr;
    ans[1]=maxx.sum;
    for(int i=2;i<=n;i++)
    {
        if(!rheap.empty() && !lheap.empty())//如果左右堆均不为空 
        {
            lsl=lheap.top();//分别记录下来左右堆的堆顶元素并进行比较 
            lsr=rheap.top();
            if(lsl.sum>lsr.sum-l)
            {
                ans[i]=lsl.sum+ans[i-1];
                lheap.pop();
            }
            else
            {
                
                ans[i]=lsr.sum-l+ans[i-1];
                rheap.pop();
                l=2*lsr.l;
                while(rheap.top().l*2<=l && !rheap.empty())
                {
                    lheap.push(rheap.top());
                    rheap.pop();
                }
            }
        }
        else
        {
            if(lheap.empty())
            {
                lsr=rheap.top();
                ans[i]=lsr.sum-l+ans[i-1];
                rheap.pop();
                l=2*lsr.l;
                while(rheap.top().l*2<=l && !rheap.empty())
                {
                    lheap.push(rheap.top());
                    rheap.pop();
                }
            }
            else
            {
                lsl=lheap.top();
                ans[i]=lsl.sum+ans[i-1];
                lheap.pop();
            } 
        } 
    }
    for(int i=2;i<=n;i++)
    {
        printf("%d\n",ans[i]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/luo_rentao/article/details/81171646