2018今日頭條演算法面試程式設計題2
阿新 • • 發佈:2019-02-03
給定一個數組序列, 需要求選出一個區間, 使得該區間是所有區間中經過如下計算的值最大的一個:
區間中的最小數 * 區間所有數的和最後程式輸出經過計算後的最大值即可,不需要輸出具體的區間。如給定序列 [6 2 1]則根據上述公式, 可得到所有可以選定各個區間的計算值:
[6] = 6 * 6 = 36;
[2] = 2 * 2 = 4;
[1] = 1 * 1 = 1;
[6,2] = 2 * 8 = 16;
[2,1] = 1 * 3 = 3;
[6, 2, 1] = 1 * 9 = 9;
從上述計算可見選定區間 [6] ,計算值為 36, 則程式輸出為 36。
區間內的所有數字都在[0, 100]的範圍內;
輸入描述:
第一行輸入陣列序列長度n,第二行輸入陣列序列。 對於 50%的資料, 1 <= n <= 10000; 對於 100%的資料, 1 <= n <= 500000;
輸出描述:
輸出陣列經過計算後的最大值。
輸入例子1:
3 6 2 1
輸出例子1:
36
題解:
這個題目想了好長時間沒有解決出來,然後最後看部落格上寫的大多數都是n方的寫法,有一個說用單調棧這次忽然明白了怎麼解,讓我想起了去年省賽選拔時的那個題目,求一個樹狀圖的最大面積。然後就又複習一下單調棧和單調的佇列。
具體思路為列舉每個點作為區間的最小值,然後利用單調棧求出區間的左右邊界,在用前n項和來求區間的和。複雜度O(nlog(n)),讓我想起了求最長遞增序列的O(nlogn)的求解過程,也是用的單調棧。
至於單調棧的思路這裡不在贅述,以前寫的題解中也有涉及單調棧的思路。
#include <bits/stdc++.h> using namespace std; const int N = 5e5+7; int l[N],r[N],sum[N],a[N]; int main(){ int n; while(~scanf("%d",&n)){ memset(sum,0,sizeof(sum)); a[0] = a[n+1] = -1; for(int i = 1;i<=n;i++){ scanf("%d",&a[i]); sum[i] = sum[i-1] + a[i]; } stack<int>s; s.push(0); for(int i = 1;i<=n;i++){ int x; for(x = s.top();a[x]>=a[i];x = s.top()){ s.pop(); } l[i] = x+1; s.push(i); } while(!s.empty())s.pop(); s.push(n+1); for(int i = n;i>=1;i--){ int x; for(x = s.top();a[x]>=a[i];x = s.top()){ s.pop(); } r[i] = x-1; s.push(i); } int mx = 0; for(int i = 1;i<=n;i++){ mx = max(mx,a[i]*(sum[r[i]]-sum[l[i]-1])); } printf("%d\n",mx); } return 0; }