子矩陣 單調佇列

2021-08-09 14:00:02 字數 1728 閱讀 8027

問題描述

小 a 有乙個 n×m 的矩陣,矩陣中 1~n*m 這(n*m)個整數均出現過一次。 現在小 a 在這個矩陣內選擇乙個子矩陣,其權值等於這個子矩陣中的所有數的最 小值。小 a 想知道,如果他選擇的子矩陣的權值為 i(1<=i<=n×m),那麼他選擇 的子矩陣可能有多少種?小 a 希望知道所有可能的 i 值對應的結果,但是這些結 果太多了,他算不了,因此他向你求助。

輸入格式

第一行,兩個整數 n,m。

接下來的 n 行,每行 m 個整數,表示矩陣中的元素。

輸出格式

n×m 行,每行乙個整數,其中第 i 行的整數表示如果小 a 選擇的子矩陣權 值為 i,他選擇的子矩陣的種類數。

資料範圍

對於 30%的資料,1<=n,m<=50;

對於全部的資料,1<=n,m<=300。

首先說說o(

n2m2

) 的暴力做法:

考慮固定矩陣左上角,如何求出以該點為左上角的所有矩陣的權值?令f[

i][j

] 表示以當前左上角,(i

,j) 為右上角構成的矩形的權值,那麼有遞推關係:f[

i][j

]=mi

n(f[

i−1]

[j],

f[i]

[j−1

],ma

p[i]

[j])

對於乙個固定的左上角,求出所有矩陣的權值的時間複雜度為o(

nm) 。左上角一共討論o(

nm) 個,所以總時間複雜度o(

n2m2

) 。

正解:從權值的角度考慮。固定矩陣上下邊界後,如何求某一權值的矩形有多少個?那麼首先要找到當前權值的點在**。顯然,當遇到左邊第乙個權值小於它的矩形就停止,這樣就找到了它的左邊界。右邊界同理。這樣的話,以該上下邊界的矩陣個數為(r

ight

[k]−

k+1)

(k−l

eft[

k]+1

) 。

以某點為中心,找左右邊界的操作可以通過單調佇列(單調棧)用o(

m)解決。列舉上下邊界的時間複雜度是o(

n2) ,總時間複雜度o(

n2m)

。當然也可以是o(

nm2)

#include

#define min(x,y) ((xint n,m,min[305],ans[90005],map[305][305],q[305],tail,l[90005],r[90005];

int main()

q[++tail]=k;

}while(tail)r[q[tail]]=m,tail--;

for(k=m;k>=1;k--)

q[++tail]=k;

}while(tail)l[q[tail]]=1,tail--;

for(k=1;k<=m;k++)ans[min[k]]+=(r[k]-k+1)*(k+1-l[k]);}}

for(i=1;i<=n*m;i++)printf("%d\n",ans[i]);

}

NKOJ 3861 子矩陣(矩陣dp 單調佇列)

問題描述 小 a 有乙個 n m 的矩陣,矩陣中 1 n m 這 n m 個整數均出現過一次。現在小 a 在這個矩陣內選擇乙個子矩陣,其權值等於這個子矩陣中的所有數的最 小值。小 a 想知道,如果他選擇的子矩陣的權值為 i 1 i n m 那麼他選擇 的子矩陣可能有多少種?小 a 希望知道所有可能的...

bzoj1047(單調佇列,矩陣中)

沒那麼難,不需要什麼二維單調佇列,其實只要改變一下順序就好 為了思路清晰,這裡採用的結構體 結構體函式寫的單調佇列 剛開始感覺不會,看hzwer的blog,上面說,題解都太麻煩,還不如手推,就沒繼續看下去,事實證明這道題回來一看就秒了,也不知道是怎麼想的。猜的?其實就是一行乙個單調佇列,端點每往右移...

單調棧,單調佇列

大多數借鑑了 單調佇列是什麼呢?可以直接從問題開始來展開。poj 2823 給定乙個數列,從左至右輸出每個長度為m的數列段內的最小數和最大數。數列長度 n 106,m n 我們知道,解法 在暴力列舉的過程中,有乙個地方是重複比較了,就是在找當前的f i 的時候,i的前面其它m 1個數在算f i 1 ...