1. 程式人生 > 實用技巧 >Acwing 293.開車旅行 (倍增優化DP)

Acwing 293.開車旅行 (倍增優化DP)

題目

小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行駛的路程總數的比值都最小,則輸出海拔最高的那個城市。

2、對任意給定的 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≤N≤105,
1≤M≤104,
−109≤Hi≤109,
0≤X0≤109,
1≤Si≤N,
0≤Xi≤109,
資料保證Hi互不相同。

輸入樣例:
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
輸出樣例:
2
3 2
2 4
2 1
2 4
5 1
5 1
2 1
2 0
0 0
0 0

思路

好難的題目,qwq,首先我們如果選定了一個城市作為起點,那麼我們接下來的ab行程將是唯一確定的,因為題目限制四了條件,相當於是一個拓撲圖了,這個資訊我們可以預處理出來。然後我們還需要處理出從某個人從某個點開始走多少步數到達的城市,這裡我們就需要倍增優化了,因為資料是1e5,暴力列舉n方會超時。然後我們最後要處理的就是最後的dp陣列,表示某個人從某個點出發走i步數,所行駛的所有距離。

程式碼實現

#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
#define rep(i,f_start,f_end) for (int i=f_start;i<=f_end;++i)
#define per(i,n,a) for (int i=n;i>=a;i--)
#define MT(x,i) memset(x,i,sizeof(x) )
#define rev(i,start,end) for (int i=start;i<end;i++)
#define inf 0x3f3f3f3f
#define mp(x,y) make_pair(x,y)
#define lowbit(x) (x&-x)
#define MOD 1000000007
#define exp 1e-8
#define N 1000005 
#define fi first 
#define se second
#define pb push_back
typedef long long ll;
typedef pair<int ,int> PII;
ll gcd (ll a,ll b) {return b?gcd (b,a%b):a; }
inline int read() {
    char ch=getchar(); int x=0, f=1;
    while(ch<'0'||ch>'9') {
        if(ch=='-') f = -1;
        ch=getchar();
    } 
    while('0'<=ch&&ch<='9') {
        x=x*10+ch-'0';
        ch=getchar();
    }   return x*f;
}

typedef pair<ll,int> PLT;
const int maxn=100010,M=17;
const ll INF=1e12;
int n;
int h[maxn];
int ga[maxn],gb[maxn];
int f[M][maxn][2];
int da[M][maxn][2],db[M][maxn][2];

void init_g () {
    set <PLT> s;
    s.insert ({INF,0}),s.insert ({INF+1,0});
    s.insert ({-INF,0}),s.insert ({-INF-1,0});

    per (i,n,1) {
        PLT t(h[i],i);
        auto j=s.lower_bound (t);
        j++;
        vector <PLT > cand;
        rev (k,0,4) {
            cand.pb (*j);
            j--;
        }
        ll d1=INF,d2=INF;
        int p1=0,p2=0;
        per (k,3,0) {
            ll d= abs (h[i]-cand[k].first);
            if (d<d1) {
                d2=d1,d1=d;
                p2=p1,p1=cand[k].second;
            }
            else if (d<d2){
               d2=d;
               p2=cand[k].second;
            }
        }
        ga[i]=p2,gb[i]=p1;
        s.insert (t);
    }
}


void init_f () {
   rev (i,0,M) 
    rep (j,1,n) {
        if (!i) f[0][j][0]=ga[j],f[0][j][1]=gb[j];
        else {
            rev (k,0,2) {
                if (i==1) f[1][j][k]=f[0][f[0][j][k]][1-k];
                else f[i][j][k]=f[i-1][f[i-1][j][k]][k];
            }
        }
    }
}

inline int get_dis (int a,int b) {
   return abs (h[a]-h[b]);
}

void init_d () {
    rev (i,0,M) 
     rep (j,1,n) {
         if (!i) {
            da[0][j][0]=get_dis (j,ga[j]),da[0][j][1]=0;
            db[0][j][1]=get_dis (j,gb[j]),db[0][j][0]=0;
         }
         else {
             rev (k,0,2) {
                 if (i==1) {
                    da[1][j][k]=da[0][j][k]+da[0][f[0][j][k]][1-k];
                    db[1][j][k]=db[0][j][k]+db[0][f[0][j][k]][1-k];
                 }
                 else {
                     da[i][j][k]=da[i-1][j][k]+da[i-1][f[i-1][j][k]][k];
                     db[i][j][k]=db[i-1][j][k]+db[i-1][f[i-1][j][k]][k];
                 }
             }
         }

     }
}

void solve (int p,int x,int &la,int &lb) {
    la=0,lb=0;
    per (i,M-1,0) {
        if (f[i][p][0]&&la+lb+da[i][p][0]+db[i][p][0]<=x) {
            la+=da[i][p][0],lb+=db[i][p][0];
            p=f[i][p][0];
        } 
    }
} 

int main () {
   cin>>n;
   rep (i,1,n) cin>>h[i];
   init_g ();
   init_f ();
   init_d ();
 
    
   int p,x;
   scanf ("%d",&x);
   int ans=0,max_h=0;
   double min_ratio=INF;

   rep (i,1,n) {
       int la,lb;
       solve (i,x,la,lb);
       double ration=lb? (double) la/lb:INF;
       if (ration<min_ratio||ration==min_ratio&&h[i]>max_h) {
           min_ratio=ration;
           max_h=h[i];
           ans=i;
       }
   }
   cout<<ans<<endl;
    int m;
    scanf ("%d",&m);
    while (m--) {
        scanf ("%d%d",&p,&x);
        int la,lb;
        solve (p,x,la,lb);
        cout<<la<<" "<<lb<<endl;
    }

   return 0;
}