1. 程式人生 > 實用技巧 >D. Discrete Centrifugal Jumps (codeforces-1407 Div. 2)

D. Discrete Centrifugal Jumps (codeforces-1407 Div. 2)

題目來源

https://codeforces.ml/contest/1407/problem/D

題意分析

  題目意思的大致可以描述為,給一個序列,長度為n,每一次操作,我們可以向該點後面的點進行跳躍,跳躍的點的要求滿足三條中的某一條條件即可。(設跳躍起點為i,跳躍終點為j,他們的高度放在a陣列中)
  1. j = i + 1;
  2. a[i]和a[j]的值小於所有的i和j之間的值(不包括i,j),即max(a[i], a[j])< min(a[i+1], a[i+1], ... , a[j-1]);
  3. a[i]和a[j]的值大於所有的i和j之間的值(不包括i,j),即min(a[i], a[j])> max(a[i+1], a[i+1], ... , a[j-1]);

  最後到達第n個點的最小操作次數。

思路分析

  當時最後還是沒有把這題寫出來。菜。。。記錄一下當時的想法。
  看到這道題,我們可以將點大致的繪製在圖上,很容易發現,當兩個點位於一個山峰(上坡+下坡)或者是一個山谷(下坡+上坡)的兩端時,那麼這兩個點就可以進行直接的跳躍。
  第一想法是dp,因為很容易想到的是,到達這個點的操作次數的最小值,等於這個點前面的能到達這個點的最小操作次數 + 1。然後就死在了不知道如何在時間複雜度內判斷能直接到達這個點的點是哪些。這個問題最後是使用單調棧去解決的,因為單調棧可以為我們維護一個以某個值為最大(最小)值的最大區間。這裡同時使用一個單調遞增棧和一個單調遞減棧。在單調遞增棧(棧頂到棧底單調遞增)中,我們每次遇到一個比棧頂元素的小的時候,就直接將這個數入棧,而當我們遇到一個比棧頂元素大或者相等的數的時候,我們看此時的棧頂元素是否比我們此時的數要小,如果小的話,說明此時棧頂這個點到我們此時遍歷到的這個數的這個區間內所有數都小於我們兩端,所以就可以從這個點直接跳躍過去。反覆進行這個操作,直至遍歷結束。單調遞減棧相同。

  當然,如果我們能找到點之間直接跳躍的關係,我們還可以建邊,走最短路。所以最大問題,或許在於建邊把。

code

 1 #include <bits/stdc++.h>
 2 
 3 #define int long long
 4 using namespace std;
 5 const int maxn = 3e5 + 7;
 6 
 7 int dp[maxn], a[maxn];
 8 
 9 signed main(){
10 //    freopen("test.txt", "r", stdin);
11     int n; scanf("%lld", &n);
12 for (int i=1; i<=n; i++) 13 scanf("%lld", &a[i]); 14 15 stack <int> u, d; 16 u.push(1); d.push(1); 17 18 for (int i=1; i<=n; i++) dp[i] = i - 1; 19 20 for (int i=2; i<=n; i++){ 21 dp[i] = dp[i-1] + 1; 22 23 while (!u.empty()){ 24 // printf("! u\n"); 25 int x = a[u.top()]; 26 if (x < a[i]) break; 27 28 u.pop(); 29 if (u.empty()) break; 30 if (a[i] < x) 31 dp[i] = min(dp[i], dp[u.top()] + 1); 32 33 } 34 u.push(i); 35 36 while (!d.empty()){ 37 // printf("! d\n"); 38 int x = a[d.top()]; 39 40 if (x > a[i]) break; 41 d.pop(); 42 if (d.empty()) break; 43 44 if (x < a[i]) 45 dp[i] = min(dp[i], dp[d.top()] + 1); 46 } 47 d.push(i); 48 // for (int j=1; j<=n; j++) printf("%lld ", dp[j]); 49 // printf("\n"); 50 } 51 52 // for (int i=1; i<=n; i++) printf("%lld ", dp[i]); 53 // printf("\n"); 54 55 printf("%lld\n", dp[n]); 56 57 return 0; 58 }