1. 程式人生 > 其它 >題解 UVA1619 【UVA1619 感覺不錯 Feel Good】

題解 UVA1619 【UVA1619 感覺不錯 Feel Good】

UVA1619 感覺不錯 Feel Good

題目大意:

給出正整數n和一個\((1 <= n <= 100 000)\)長度的數列,要求找出一個子區間,使這個子區間的數字和乘上子區間中的最小值最大。輸出這個最大值與區間的兩個端點。

solution:

很暴力的去想,我們可以列舉區間\([l,r]\),遍歷這個區間找到最小值,同時算出區間和。但很明顯時間複雜度爆炸\(O(N^3)\)

我們再審一遍題:

使這個子區間的數字和乘上子區間中的最小值最大

既然列舉區間行不通,不妨試著列舉最小值:對於每一個數列中的數字 \(a_i\) ,找到當它作為數列最小值時最長的序列,得到序列的端點 \(l,r\)

。預處理字首和,通過\(a[r]-a[l-1]\)求出區間的和,乘這個最小值\(a[i]\)最後取一個\(max\)

接下來是細節的處理:

我們想要找到當\(a[i]\)作為最小值的最長序列,就要找到\(a[i]\)左邊、右邊最後一個比它大的數,或者說找到第一個比它小的數,左右分別為\(a[l]\)\(a[r]\),我們想要得到的左端點、右端點就為:\(l+1\)\(r-1\)

現在我們請出主角:單調棧
在這不多做贅述,不會的自己看看吧

具體操作見程式碼:

//找右端第一個比a[i]小的數,把右端點賦成i-1
for(int i=1;i<=n;i++)
{
    while(top&&a[i]<a[st[top]]) you[st[top--]]="i-1;" st[++top]="i;" }="" while(top)="" 找左端第一個比a[i]小的數,把左端點賦成i+1="" for(int="" i="n;i">=1;i--)
{
	while(top&&a[i]<a[st[top]]) zuo[st[top--]]="i+1;" st[++top]="i;" }="" while(top)="" ```="" 再處理下字首和="" ```cpp="" for(int="" i="1;i<=n;i++)" {="" scanf("%lld",&a[i]);="" p[i]="p[i-1]+a[i];" ~~完結撒……~~**此題還有很多坑點**="" uva是多組輸入="" 我們需要="" while(scanf("%d",&n)!="EOF)" 此題**沒有**spj,需要在最大值相同時,使區間儘量**短**,於是出現了下面的判斷:="" long="" now;="" 不開long="" long見祖宗="" now="a[i]*(p[you[i]]-p[zuo[i]-1]);//當前乘積" if(now="">ans||(now==ans&&you[i]-zuo[i]</a[st[top]])></a[st[top]])>