noip2012 開車旅行


P1081 開車旅行

    • 139通過
    • 484提交
  • 題目提供者洛谷OnlineJudge
  • 標簽倍增2012NOIp提高組
  • 難度提高+/省選-

提交該題 討論 題解 記錄

 

題目描述

小 A 和小 B 決定利用假期外出旅行,他們將想去的城市從 1 到 N 編號,且編號較小的

城市在編號較大的城市的西邊,已知各個城市的海拔高度互不相同,記城市 i 的海拔高度為

Hi,城市 i 和城市 j 之間的距離 d[i,j]恰好是這兩個城市海拔高度之差的絕對值,即

d[i,j] = |Hi− Hj|。

旅行過程中,小 A 和小 B 輪流開車,第一天小 A 開車,之后每天輪換一次。他們計划

選擇一個城市 S 作為起點,一直向東行駛,並且最多行駛 X 公里就結束旅行。小 A 和小 B

的駕駛風格不同,小 B 總是沿着前進方向選擇一個最近的城市作為目的地,而小 A 總是沿

着前進方向選擇第二近的城市作為目的地(注意:本題中如果當前城市到兩個城市的距離

相同,則認為離海拔低的那個城市更近)。如果其中任何一人無法按照自己的原則選擇目的

城市,或者到達目的地會使行駛的總距離超出 X 公里,他們就會結束旅行。

在啟程之前,小 A 想知道兩個問題:

1.對於一個給定的 X=X0,從哪一個城市出發,小 A 開車行駛的路程總數與小 B 行駛

的路程總數的比值最小(如果小 B 的行駛路程為 0,此時的比值可視為無窮大,且兩個無窮大視為相等)。如果從多個城市出發,小 A 開車行駛的路程總數與小 B 行駛的路程總數的比

值都最小,則輸出海拔最高的那個城市。

  1. 對任意給定的 X=Xi和出發城市 Si,小 A 開車行駛的路程總數以及小 B 行駛的路程

總數。

輸入輸出格式

輸入格式:

 

第一行包含一個整數 N,表示城市的數目。

第二行有 N 個整數,每兩個整數之間用一個空格隔開,依次表示城市 1 到城市 N 的海

拔高度,即 H1,H2,……,Hn,且每個 Hi都是不同的。

第三行包含一個整數 X0。

第四行為一個整數 M,表示給定 M 組 Si和 Xi。

接下來的 M 行,每行包含 2 個整數 Si和 Xi,表示從城市 Si出發,最多行駛 Xi公里。

 

輸出格式:

 

輸出共 M+1 行。

第一行包含一個整數 S0,表示對於給定的 X0,從編號為 S0的城市出發,小 A 開車行駛

的路程總數與小 B 行駛的路程總數的比值最小。

接下來的 M 行,每行包含 2 個整數,之間用一個空格隔開,依次表示在給定的 Si和

Xi下小 A 行駛的里程總數和小 B 行駛的里程總數。

 

輸入輸出樣例

輸入樣例#1:
drive1
4 
2 3 1 4 
3 
4 
1 3 
2 3 
3 3 
4 3


drive2
 10 
4 5 6 1 2 3 7 8 9 10 
7 
10 
1 7 
2 7 
3 7 
4 7 
5 7 
6 7 
7 7 
8 7 
9 7 
10 7
輸出樣例#1:
drive1
1 
1 1 
2 0 
0 0 
0 0 

drive2
2 
3 2 
2 4 
2 1 
2 4 
5 1 
5 1 
2 1 
2 0 
0 0 
0 0

說明

【輸入輸出樣例 1 說明】

各個城市的海拔高度以及兩個城市間的距離如上圖所示。

如果從城市 1 出發,可以到達的城市為 2,3,4,這幾個城市與城市 1 的距離分別為 1,1,2,

但是由於城市 3 的海拔高度低於城市 2,所以我們認為城市 3 離城市 1 最近,城市 2 離城市

1 第二近,所以小 A 會走到城市 2。到達城市 2 后,前面可以到達的城市為 3,4,這兩個城

市與城市 2 的距離分別為 2,1,所以城市 4 離城市 2 最近,因此小 B 會走到城市 4。到達城

市 4 后,前面已沒有可到達的城市,所以旅行結束。

如果從城市 2 出發,可以到達的城市為 3,4,這兩個城市與城市 2 的距離分別為 2,1,由

於城市 3 離城市 2 第二近,所以小 A 會走到城市 3。到達城市 3 后,前面尚未旅行的城市為

4,所以城市 4 離城市 3 最近,但是如果要到達城市 4,則總路程為 2+3=5>3,所以小 B 會

直接在城市 3 結束旅行。

如果從城市 3 出發,可以到達的城市為 4,由於沒有離城市 3 第二近的城市,因此旅行

還未開始就結束了。

如果從城市 4 出發,沒有可以到達的城市,因此旅行還未開始就結束了。

【輸入輸出樣例 2 說明】

當 X=7 時,

如果從城市 1 出發,則路線為 1 -> 2 -> 3 -> 8 -> 9,小 A 走的距離為 1+2=3,小 B 走的

距離為 1+1=2。(在城市 1 時,距離小 A 最近的城市是 2 和 6,但是城市 2 的海拔更高,視

為與城市 1 第二近的城市,所以小 A 最終選擇城市 2;走到 9 后,小 A 只有城市 10 可以走,

沒有第 2 選擇可以選,所以沒法做出選擇,結束旅行)

如果從城市 2 出發,則路線為 2 -> 6 -> 7 ,小 A 和小 B 走的距離分別為 2,4。

如果從城市 3 出發,則路線為 3 -> 8 -> 9,小 A 和小 B 走的距離分別為 2,1。

如果從城市 4 出發,則路線為 4 -> 6 -> 7,小 A 和小 B 走的距離分別為 2,4。

如果從城市 5 出發,則路線為 5 -> 7 -> 8 ,小 A 和小 B 走的距離分別為 5,1。

如果從城市 6 出發,則路線為 6 -> 8 -> 9,小 A 和小 B 走的距離分別為 5,1。

如果從城市 7 出發,則路線為 7 -> 9 -> 10,小 A 和小 B 走的距離分別為 2,1。

如果從城市 8 出發,則路線為 8 -> 10,小 A 和小 B 走的距離分別為 2,0。

全國信息學奧林匹克聯賽(NOIP2012)復賽

提高組 day1

第 7 頁 共 7 頁

如果從城市 9 出發,則路線為 9,小 A 和小 B 走的距離分別為 0,0(旅行一開始就結

束了)。

如果從城市 10 出發,則路線為 10,小 A 和小 B 走的距離分別為 0,0。

從城市 2 或者城市 4 出發小 A 行駛的路程總數與小 B 行駛的路程總數的比值都最小,

但是城市 2 的海拔更高,所以輸出第一行為 2。

【數據范圍】

對於 30%的數據,有 1≤N≤20,1≤M≤20;

對於 40%的數據,有 1≤N≤100,1≤M≤100;

對於 50%的數據,有 1≤N≤100,1≤M≤1,000;

對於 70%的數據,有 1≤N≤1,000,1≤M≤10,000;

對於100%的數據,有1≤N≤100,000,1≤M≤10,000,-1,000,000,000≤Hi≤1,000,000,000,

0≤X0≤1,000,000,000,1≤Si≤N,0≤Xi≤1,000,000,000,數據保證 Hi互不相同。

NOIP 2012 提高組 第一天 第三題

分析:這道題不會做,看別人題解看了一下午,算是有一點心得吧,分享一下,首先看數據,似乎可以暴力個部分分,每次選取一個點就開始模擬行走.每次都要尋找第一近和第二近,非常浪費時間,那么就預處理,怎么預處理呢?排序嗎?有點復雜,離得最近的一定是高度差相近的,那么能排序並且在相近的元素中尋找的數據結構是什么呢?STL的set!每次找兩個前驅和后繼,然后排序取前兩個.不過要注意,插入set要倒着插,因為每次都是去東邊的城市.但是這樣預處理之后還是不能通過所有的數據,我們來對算法仔細的分析,查找第一近和第二近的優化過了,枚舉點似乎不能優化,那么只能優化模擬行走了.會發現兩次行走可以合並成一次,有沒有什么更快的枚舉技巧呢?二分?似乎不行.那么就是倍增了.設f[i][j]為在位置i走2^j輪的位置,一輪就是A走和B走一次,fa[i][j]就是A在位置i走2^j輪的距離,fb[i][j]類同,那么怎么算呢?記住一個重要的性質:a^i-1 + a^i-1 = a^i,遞推也可以這樣,f[i][j] = f[f[i][j-1]][j-1],fa[i][j] = fa[i][j-1] + fa[f[i][j-1]][j-1],fb類同,因為n<=100000,所以2^i,i從19開始枚舉,從大到小枚舉,依次判斷行不行即可.(真是復雜......)

      對枚舉一個常見的優化:倍增.

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>

using namespace std;

const int maxn = 100005;
int n, x0, m, k,x1, na[maxn], nb[maxn],ans;
long long ansa = 1e5,ansb = 0ll,fa[maxn][20], fb[maxn][20], f[maxn][20];


struct node
{
    int id, high;
    bool operator < (const node & b) const {
        return high < b.high;
    }
}c[maxn];

struct node2
{
    int id, gaoducha;
    bool operator < (const node2 & b) const {
        if (gaoducha != b.gaoducha)
            return gaoducha < b.gaoducha;
        else
            return c[id].high < c[b.id].high;
    }
}temp[5];

set <node> s;

void init(int i)
{
    set <node> ::iterator it = s.find(c[i]);
    int j = 0;
    if (it != s.begin())
    {
        --it;
        temp[++j] = (node2) { it->id, abs(it->high - c[i].high) };
        if (it != s.begin())
        {
            --it;
            temp[++j] = (node2) { it->id, abs(it->high - c[i].high) };
            ++it;
        }
        ++it;
    }
    if ((++it) != s.end())//s.end()是結尾之后的!
    {
        temp[++j] = (node2) { it->id, abs(it->high - c[i].high) };
        if ((++it) != s.end())
            temp[++j] = (node2) { it->id, abs(it->high - c[i].high) };
    }
    sort(temp + 1, temp + j + 1);
    nb[i] = temp[1].id;
    if (j == 1)
        return;
    na[i] = temp[2].id;
    return;
}

void query(int st, int x, long long &ta, long long &tb)
{
    for (int i = 19; i >= 0; i--)
        if (f[st][i] && fa[st][i] + fb[st][i] <= x)
        {
            ta += fa[st][i];
            tb += fb[st][i];
            x -= fa[st][i] + fb[st][i];
            st = f[st][i];
        }
    if (!na[st])
        return;
    int tempans = abs(c[na[st]].high - c[st].high);
    if (tempans <= x)
        ta += tempans;
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &c[i].high);
        c[i].id = i;
    }
    for (int i = n; i;i--)
    {
        s.insert(c[i]);
        if (i != n)
            init(i);
    }
    for (int i = 1; i <= n; i++)
    {
        int p1 = na[i], p2 = nb[na[i]];
        fa[i][0] = p1 ? abs(c[p1].high - c[i].high) : 0;
        fb[i][0] = p2 ? abs(c[p2].high - c[p1].high) : 0;
        f[i][0] = p2;
    }
    for (int j = 1; j < 20;j++) //后更新的是j所以先枚舉j
        for (int i = 1; i <= n; i++)
        {
            f[i][j] = f[f[i][j - 1]][j - 1];
            fa[i][j] = fa[i][j - 1] + fa[f[i][j - 1]][j - 1];
            fb[i][j] = fb[i][j - 1] + fb[f[i][j - 1]][j - 1];
        }
    scanf("%d", &x0);
    ans = 0;
    for (int i = 1; i <= n; i++)
    {
        long long ta = 0ll, tb = 0ll;
        query(i, x0, ta, tb);
        if (tb && (!ans || ansa * tb > ansb * ta))
        {
            ansa = ta;
            ansb = tb;
            ans = i;
        }
    }
    printf("%d\n", ans);
    scanf("%d", &m);
    while (m--)
    {
        scanf("%d%d", &k, &x1);
        long long ta = 0ll, tb = 0ll;
        query(k, x1, ta, tb);
        printf("%d %d\n", ta, tb);
    }

    return 0;
}

orz神犇raffica


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM