1. 程式人生 > 實用技巧 >51nod 1279 扔盤子

51nod 1279 扔盤子

題目網址:http://class.51nod.com/Challenge/Problem.html#problemId=1279

一、題目描述

有一口井,井的高度為N,每隔1個單位它的寬度有變化。

現在從井口往下面扔圓盤,如果圓盤的寬度大於井在某個高度的寬度,則圓盤被卡住(恰好等於的話會下去)。

盤子有幾種命運:1、掉到井底。2、被卡住。3、落到別的盤子上方。

盤子的高度也是單位高度。給定井的寬度和每個盤子的寬度,求最終落到井內的盤子數量。

如圖井和盤子資訊如下:

井:5 6 4 3 6 2 3

盤子:2 3 5 2 4

最終有4個盤子落在井內。

輸入描述

第1行:2個數N, M中間用空格分隔,N為井的深度,M為盤子的數量(1 <= N, M <= 50000)。
第2 - N + 1行,每行1個數,對應井的寬度Wi(1 <= Wi <= 10^9)。
第N + 2 - N + M + 1行,每行1個數,對應盤子的寬度Di(1 <= Di <= 10^9)

輸出描述

輸出最終落到井內的盤子數量。

樣例輸入

7 5
5 6 4 3 6 2 3
2 3 5 2 4

樣例輸出

4

二、解題思路

正常思路是對每一個盤子從上往下遍歷井,這樣複雜度是O(n2) ,想一下怎麼優化。


考慮到一個盤子,如果能過寬的層下面的窄層,那麼上面那個寬層肯定能過,所以從上往下的時候決定
它能不能過的其實是一個寬度遞減的序列,所以先預處理一下井,改為遞減,存入單調棧中。然後逆向
思考,從最下面的那個寬度開始遍歷,複雜度O(n) 。

我們定義一個單調棧s,裡面存的是對於每一個井口(w陣列)盤子最大多大能進去

for(int i = 0;i < n;i++){
    if(w[i] < tmp){
        tmp 
= w[i]; } s.push(tmp); }

可是單調棧他自動給你轉化成了升序(反過來了)。這樣待會輸出的時候就方便了許多。

輸出的時候他把每個井口最大能通過的盤子跟現在這個盤子進行對比,最後進行輸出

while(!s.empty() && cur<m){
    if(s.top() < d[cur]){
        s.pop();
        continue;
    }
    ans++;
    s.pop();
    cur++;
}

完整程式碼:

#include<stack>
#include<cstdio>
#include
<iostream> using namespace std; int n, m, tmp, cur, w[50005], d[50005]; stack <int> s; int main(){ cin >> n >> m; for(int i = 0;i < n;i++){ cin >> w[i]; } for(int i = 0;i < m;i++){ cin >> d[i]; } for(int i = 0;i < n;i++){ if(w[i] < tmp){ tmp = w[i]; } s.push(tmp); } while(!s.empty() && cur<m){ if(s.top() < d[cur]){ s.pop(); continue; } ans++; s.pop(); cur++; } cout << ans << endl; return 0; }