Skip to content

滑動視窗(Sliding window)(技巧)

基本上是以兩個指標維護一段區間,然後逐步將這區間從前往後(從左往右)移動。維護兩個指標這一類的方法也有 人稱為雙指標法,但也包含兩個指標從兩端往中間移動(ap325)

d031: 例題 P-3-7. 正整數序列之最接近的區間和

https://judge.tcirc.tw/ShowProblem?problemid=d031

輸入一個正整數序列 (A[1],A[2],…,A[n]) ,另外給了一個非負整數 K ,請計算哪些連續區段的和最接近 K 而不超過 K ,以及這樣的區間有幾個。

n 不超過 20 萬,輸入數字與 K 皆不超過 10 億。

輸入說明
第一行是 n 與 K ,
第二行 n 個整數是 A[i] ,同行數字以空白間隔。

輸出說明
第一行輸出最接近 K 但不超過 K 的和,
第二行輸出這樣的區間有幾個

範例輸入:
5 10
4 3 1 7 4

範例輸出:
8
2

這題是用兩個指針一直維持一個區間,右指針不停往右移動,透過縮小/放大(移動左指針)區間來維持條件

移動右指針方法可以用類似貪心演算法的方式
因為只會往右一個位置,用for迴圈比較方便

code
#include <bits/stdc++.h>
using namespace std;
#define nn "\n"
int v[300000];
int main(){
  int n,m,ans=0;
  cin>>n>>m;
  for(int i=0;i<n;i++){
    cin>>v[i];
  }
  int sum=0;
  map<int,int>mp;
  for(int l=0,r=0;r<n;r++){
    sum+=v[r];
    while(sum>m){
      sum-=v[l];
      l++;
    }
    mp[sum]++;
  }
  cout<<nn<<mp.rbegin()->first;   //輸出最後一個的first
  cout<<nn<<mp.rbegin()->second;  //輸出最後一個的second
}

a175: P-3-8. 固定長度區間的最大區段差

內容
對於序列的一個連續區段來說,區段差是指區段內的最大值減去區段內的最小值。

有N個非負整數組成的序列seq,請計算在所有長度為L的連續區段中,最大的區段差為何。

輸入說明
第一行是N與L,第二行是序列內容,相鄰數字間以空白隔開。

L≤N≤2e5,數字不超過1e9。

輸出說明
輸出所求的最大區間差。

範例輸入 #1
9 4
1 4 3 6 9 8 5 7 1
範例輸出 #1
7

multiset解法

會有重複所以用multiset

multiset
#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
#define all(x) x.begin(),x.end()
#define nn "\n"
#define int long long


signed main(){
    //istringstream cin("9 4 \
1000 4 3 1 9 1 5 7 100");
    int n,m;
    cin>>n>>m;


    vector<int>v(n);

    for(int i=0;i<n;i++){
        cin>>v[i];
    }


    multiset<int>s;



    // 初始化前 m 個元素
    for (int i = 0; i < m; i++) {
        s.insert(v[i]);
    }

    int ans=-1;

    ans=max(ans,abs(*s.begin()-*s.rbegin()));

    for(int i=m;i<n;i++){
        s.insert(v[i]);
        auto it=s.find(v[i-m]);
        s.erase(it);

        ans=max(ans,abs(*s.begin()-*s.rbegin()));
    }

    cout<<ans;

}

deque解法

deque:from AP325
#include<bits/stdc++.h>
using namespace std;

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int n, m;
    cin >> n >> m;
    vector<int> v(n);

    for (int i = 0; i < n; i++) {
        cin >> v[i];
    }

    deque<int> madq;  // 維護最大值的雙端佇列
    deque<int> midq;  // 維護最小值的雙端隊列

    int ans = INT_MIN;

    for (int i = 0; i < n; i++) {
        // 維護最大值隊列
        if (!madq.empty() && madq.front() <= i - m) {
            madq.pop_front();
        }
        while (!madq.empty() && v[madq.back()] <= v[i]) {
            madq.pop_back();
        }
        madq.push_back(i);

        // 維護最小值隊列
        if (!midq.empty() && midq.front() <= i - m) {
            midq.pop_front();
        }
        while (!midq.empty() && v[midq.back()] >= v[i]) {
            midq.pop_back();
        }
        midq.push_back(i);

        // 當視窗的大小達到m時,計算目前的最大區段差
        if (i >= m - 1) {
            ans = max(ans, v[madq.front()] - v[midq.front()]);
        }
    }

    cout << ans << endl;

    return 0;
}
deque:自己寫
#include<bits/stdc++.h>
using namespace std;

struct st{
    int x,y; //value,index
};

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    int n,m,ans=INT_MIN;
    cin>>n>>m;

    deque<st>dqma;
    deque<st>dqmi;


    for(int i=0;i<n;i++){
        int x;
        cin>>x;

        if(!dqma.empty() && i-dqma.front().y>=m){
            dqma.pop_front();
        }
        while(!dqma.empty() &&dqma.back().x<x){
            dqma.pop_back();
        }
        dqma.push_back({x,i});


        if(!dqmi.empty() &&i-dqmi.front().y>=m){
            dqmi.pop_front();
        }
        while(!dqmi.empty() &&dqmi.back().x>x){
            dqmi.pop_back();
        }
        dqmi.push_back({x,i});
        
        ans=max(ans,dqma.front().x-dqmi.front().x);
    }

    cout<<ans;
    return 0;
}