我是靠谱客的博主 清脆菠萝,最近开发中收集的这篇文章主要介绍NOIP 2012 Senior 3 - 开车旅行,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

还是太弱了…这道题拿到手上后完全没有能够在规定时间内解决的思路。不过还好,大体思路是对的:
首先预处理A、B在每个地方开一次车到达的地方。对于第一问,枚举A出发的位置,对于第二问,直接计算就行了。计算的方法就是挨着推,直到满足题目中结束旅行的条件。很明显,预处理的时间复杂度为 O(n2) ,计算的时间复杂度为 O(n) ,因此整个程序的时间复杂度就为 O(n2+n2+nm) ,平方级的,按理说还能过个50~70分,但是可能比赛时计算比例那里写错了,最后只有35分。

这个题的思路就是这样,我们要做的就是将上面的两个操作至少优化到 O(nlogn) ,幸运的是,这种算法是存在的。对于操作1,我们可以用一棵平衡树以 O(logn) 的时间复杂度完成操作,但我连平衡树都没有学过= =。幸运的是,STL有一个现成的平衡树set,使用它就已足够满足我们的要求了。
由于旅行只能从左往右走,因此我们只能从右往左向set中添加结点。假设有5座城市1,2,3,4,5,我们要计算A、B各自从3出发到达的下一个城市:

  3
 / 
4   5

这里假设4比3低,5比3高,那么树将会是以上这样。
我们可以使用set的find方法找到3的位置,然后对迭代器进行++,–,就能找到4,5了。
那么到底A该走哪里,B该走哪里呢?假设我们要计算1:

    1
   / 
  3   5              //这棵树是我乱编的
 /     
4       2

所以我们现在知道的是可能的答案为3,4,5,2,即向左扩展2个结点,向右扩展2个结点(想一想,为什么)。我们对这四个结点的进行排序,第0个就是B的目的地,第1个就是A的目的地。当然,如果A没有目的地,就不要改A。

对于计算这个操作,我们可以使用倍增的思想。之前我们一次走一步,时间复杂度为 O(n) 。倍增的思想就是花上 O(nlogn) 的时间进行预处理,使每次的查询时间将为 O(logn) 。对于这道题而言,可以这样定义:

next[k][i]代表从i出发,AB开了2^k轮车后到达的位置(一轮指AB先后开了一次车)
disA[k][i]代表从i出发,AB开了2^k轮车后A走的里程
disB[k][i]代表从i出发,AB开了2^k轮车后B走的里程
如果遇到最后只有A开了车,需要单独计算

有没有感觉这和ST表的定义很像?事实上,ST表就是用的倍增的思想。
枚举时,我们将k从大到小进行枚举(想一想,为什么不能从小到大)。如果开2^k轮车后能走到一个地方,则开,否则k--。k最终枚举到0,这毋庸置疑,那k从哪里开始枚举呢?我们可以从可能的极大值开始。因为如果k很大,肯定是开不到的,只是浪费一点时间而已。

最后,我们根据初始化的信息推算即可,详见代码。

参考代码

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
using std::cin;
using std::cout;
using std::endl;
#define FOR(x, f, t) for(int x = (f); x <= (t); x++)
inline int readIn()
{
    int a;
    scanf("%d",&a);
    return a;
}

const int maxn = 100005;
const int maxm = 100005;
int n,m;
struct CITY
{
    int index;
    int height;

    CITY(int index = 0, int height = 0):index(index), height(height)
    {

    }
    bool operator< (const CITY& b) const
    {
        return height < b.height;
    }
} cities[maxn];
int start[maxm];
int x[maxm];

int height;
struct sub   //用于初始化
{
    CITY destination;
    void operator=(const CITY& b)
    {
        destination = b;
    }
    bool operator<(const sub& b) const
    {
        int s1 = std::abs(height - destination.height);
        int s2 = std::abs(height - b.destination.height);
        if(s1==s2) return destination.height < b.destination.height;
        return s1<s2;
    }
} subs[4];
int nextA[maxn];
int nextB[maxn];
const int maxIndex = 17;
int next[maxIndex][maxn]; //从i开2^k轮后到的位置
long long disA[maxIndex][maxn]; //从i开2^k轮A的路程
long long disB[maxIndex][maxn]; //从i开2^k轮B的路程

void input()
{
    n=readIn();
    FOR(i, 1, n)
    {
        cities[i].height=readIn();
        cities[i].index = i;
    }
    x[0]=readIn();
    m=readIn();
    FOR(i, 1, m)
    {
        start[i]=readIn();
        x[i]=readIn();
    }
}
void init()
{
    std::set<CITY> des;
    des.insert(cities[n]);

    for(int i = n-1; i >= 1; i--)   //要从右往左更新,因为只能从左往右走
    {
        des.insert(cities[i]); //先插入:我们要知道要更新的城市的位置
        std::set<CITY>::iterator it = des.find(cities[i]); //要更新的城市的位置

        int nCmp = 0;
        if(it!=des.begin())
        {
            it--;
            subs[nCmp++] = *it;
            if(it!=des.begin())
            {
                it--;
                subs[nCmp++] = *it;
                it++;
            }
            it++;
        }
        if(it!=des.end())
        {
            it++; //不用复原了
            subs[nCmp++] = *it;
            if(it!=des.end())
            {
                it++;
                subs[nCmp++] = *it;
            }
        }

        height = cities[i].height;
        std::sort(subs, subs+nCmp);
        nextB[i] = subs[0].destination.index;
        if(i<=n-2) nextA[i] = subs[1].destination.index;
    }
}

void go(int from, int X, long long& lengthA, long long& lengthB)
{
    for(int i = maxIndex - 1; ~i; i--) //相当于二进制
    {
        if(next[i][from] && disA[i][from] + disB[i][from] <= X)
        {
            lengthA += disA[i][from];
            lengthB += disB[i][from];
            X -= disA[i][from] + disB[i][from];
            from = next[i][from];
        }
    }
    int last = nextA[from]; //看看A还能不能走
    if(!last) return;
    int lastDis = std::abs(cities[last].height - cities[from].height);
    if(lastDis <= X) lengthA+=lastDis;
}

void run()
{
    input();
    init(); //初始化A,B开一次开到哪里去了

    for(int i = 1; i <= n; i++) //初始化开1轮的情况
    {
        int pos1 = nextA[i];
        int pos2 = nextB[pos1];
        if(pos1) disA[0][i] = std::abs(cities[pos1].height - cities[i].height);
        if(pos2) disB[0][i] = std::abs(cities[pos2].height - cities[pos1].height);
        next[0][i] = pos2;
    }
    for(int i = 1; i <= maxIndex - 1; i++) //初始化开2^k轮的情况
    {
        for(int j = 1; j <= n; j++)
        {
            next[i][j] = next[i - 1][next[i - 1][j]];
            disA[i][j] = disA[i - 1][j] + disA[i - 1][next[i - 1][j]];
            disB[i][j] = disB[i - 1][j] + disB[i - 1][next[i - 1][j]];
        }
    }

    //solve1
    long long ansA=1e15;
    long long ansB=0;
    int ans = 0;
    for(int i = 1; i <= n; i++)
    {
        long long lengthA=0, lengthB=0;
        go(i, x[0], lengthA, lengthB);
        if(lengthB && (!ans || ansA * lengthB > ansB * lengthA))
        {
            ans = i;
            ansA = lengthA;
            ansB = lengthB;
        }
    }
    printf("%dn",ans);

    //solve2
    for(int i = 1; i <= m; i++)
    {
        long long lengthA=0, lengthB=0;
        go(start[i], x[i], lengthA, lengthB);
        cout<<lengthA<<" "<<lengthB<<endl;
        printf("%lld %lldn",lengthA, lengthB);
    }
}

int main()
{
    run();
    return 0;
}

最后

以上就是清脆菠萝为你收集整理的NOIP 2012 Senior 3 - 开车旅行的全部内容,希望文章能够帮你解决NOIP 2012 Senior 3 - 开车旅行所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(65)

评论列表共有 0 条评论

立即
投稿
返回
顶部