單調棧優化dp:hdu1506Largest Rectangle in a Histogram &hdu1505city game dp
阿新 • • 發佈:2019-01-07
其實早在去年九月就做過這兩個題了
然而並不是用單調棧來優化的,一個很不好理解的方法做的,要是讓我再寫一遍估計還是一臉懵逼QAQ直到昨天晚上看了小島的直播雖說講的實在太快,但是單調棧這玩意貌似真得好好學學
先通過講第一題說明單調棧的維護:
看示例陣列:7,2,1,4,5,1,2,3 最大子矩陣的面積是8
在遍歷整個陣列的時候維護一個遞增棧,儲存比當前數字小的數值。為什麼?既然是圍出來的,那就一定是矩形的左右邊是最短和第二短的,我們可以通過找兩個比較短的圍出來的矩形(這兩個短邊中間的邊都不比這兩條短)來確定圍出的面積。如果右側的邊是最小的邊,那麼這條邊可以作為右邊界與左側截止到比這個邊小的邊中間的所有較長的邊圍出矩形,而中間的這些邊之前我們遍歷過的了,遍歷的時候就可以維護一個遞增棧保留數字,具體的陣列維護過程:
先將0入棧(陣列下標從1開始)表示最左側0位置的數值0進入棧;遍歷每個數字時,若發現棧頂元素比當前元素大,出棧,計算並更新最大面積(面積的高是棧頂元素,寬度是棧頂元素距離當前元素的距離-1,-1是因為棧頂元素是作為較小值的),持續此動作直到棧頂元素比這個元素的值小;每遍歷一個數字將它加入棧中
#include <iostream> #include<cstdio> #include<stack> using namespace std; const int N=int(1e5)+9; int h[N]; int n; int main() { while(cin>>n&&n) { for(int i=1;i<=n;i++)scanf("%d",&h[i]); long long ans=0; stack<int>s;s.push(0);h[++n]=0; for(int i=1;i<=n;i++) { while(h[i]<h[s.top()]) { long long a=h[s.top()];s.pop(); long long b=i-s.top()-1; if(a*b>ans)ans=a*b; } s.push(i); } printf("%lld\n",ans); } return 0; }
下一個題,改成了二維中找出最大面積。抽象出這樣的模型:只要是找到的一個滿足條件的矩形,它的下邊界就可以看做是上一個題的底邊。這麼想就好辦了,列舉每一行 ,上面處理出來上一題的圖形,沒了……
#include <iostream> #include<cstdio> #include<cstring> #include<stack> using namespace std; int h[1009],n,m,t; char str[1009]; int main() { // freopen("cin.txt","r",stdin); scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); memset(h,0,sizeof(h)); int ans=0; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { scanf("%s",str); if(i!=1) { if(str[0]=='F')h[j]=h[j]+1; else h[j]=0; } else { if(str[0]=='F')h[j]=1; else h[j]=0; } } // for(int j=1;j<=m;j++)printf("i=%d,j=%d,h=%d\n",i,j,h[j]); stack<int>s;s.push(0);h[m+1]=0; for(int j=1;j<=m+1;j++) { while(h[j]<h[s.top()]) { int a=h[s.top()];s.pop(); int b=j-s.top()-1; if(a*b>ans)ans=a*b; } s.push(j); } } printf("%d\n",ans*3); } return 0; }