日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[ZJOI2007] 棋盘制作(单调栈 / DP悬线法)

發(fā)布時(shí)間:2023/12/3 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [ZJOI2007] 棋盘制作(单调栈 / DP悬线法) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

problem

洛谷鏈接

solution1-單調(diào)棧

很容易想到,預(yù)處理出每個(gè)點(diǎn)向上最大能延伸的長度,然后對(duì)每個(gè)點(diǎn)求一個(gè)矩陣面積。

然后思考優(yōu)化,不難想到每次對(duì)一行進(jìn)行求解。

每一行的所有列一起構(gòu)成了一個(gè)直方圖。

直方圖直接經(jīng)典笛卡爾樹。笛卡爾樹性質(zhì)是什么?是單調(diào)棧!

直接對(duì)每一行都建一個(gè)單調(diào)棧,遍歷列維護(hù)遞增棧,彈出時(shí)計(jì)算答案。正方形將兩邊取個(gè) min?\minmin 再比就行了。

這個(gè)做法很簡單,但是直觀上是 O(n3)O(n^3)O(n3) 的,所以很多人不敢打。

但實(shí)際上跑很快,因?yàn)槊總€(gè)點(diǎn)只會(huì)被做一次。一次單調(diào)棧是做連續(xù)一段。

時(shí)間復(fù)雜度應(yīng)該是 O(n2)O(n^2)O(n2) 的。

#include <bits/stdc++.h> using namespace std; #define maxn 2005 int n, m, top, ans1, ans2; int h[maxn], s[maxn]; int c[maxn][maxn];int main() {scanf( "%d %d", &n, &m );for( int i = 1;i <= n;i ++ )for( int j = 1;j <= m;j ++ )scanf( "%d", &c[i][j] );for( int i = 1;i <= n;i ++ ) { //枚舉每一行for( int j = 1;j <= m;j ++ ) //預(yù)處理該行上每一列最長能向上延伸的長度if( i > 1 and c[i][j] ^ c[i - 1][j] ) ++ h[j];else h[j] = 1;for( int j = 1, k;j <= m;j = k ) {s[0] = j - 1, s[top = 1] = j;for( k = j + 1;k <= m and c[i][k] ^ c[i][k - 1];k ++ ) {while( top and h[s[top]] > h[k] ) {ans1 = max( ans1, min(h[s[top]], k - s[top - 1] - 1) * min(h[s[top]], k - s[top - 1] - 1) );ans2 = max( ans2, h[s[top]] * (k - s[top - 1] - 1) );top --;}s[++ top] = k;}while( top ) {ans1 = max( ans1, min(h[s[top]], k - s[top - 1] - 1) * min(h[s[top]], k - s[top - 1] - 1) );ans2 = max( ans2, h[s[top]] * (k - s[top - 1] - 1) );top --;}}}printf( "%d\n%d\n", ans1, ans2 );return 0; }

solution2-DP懸線法

最大子矩陣很熟悉啊!是 DPDPDP 入門必講的經(jīng)典例題。

對(duì)每一個(gè)位置 (i,j)(i,j)(i,j) 預(yù)處理出其向上/向左/向右分別能延伸到多遠(yuǎn)。

然后詢問每一個(gè)位置 (i,j)(i,j)(i,j) 被包含的最大子矩陣,如果和上一行是合法的就要結(jié)合上一行的左右限制。

具體而言,左邊限制取較大值,右邊限制取較小值,本質(zhì)是取最靠近 (i,j)(i,j)(i,j) 的左右兩邊限制。

取完后,矩形長即 r(i,j)?l(i,j)+1r(i,j)-l(i,j)+1r(i,j)?l(i,j)+1,寬即 h(i,j)h(i,j)h(i,j)。就可以求了。

這里需要注意的是,不能在預(yù)處理的時(shí)候,就考慮上一行的左右限制。

//這么寫就是錯(cuò)的 for( int i = 1;i <= n;i ++ )for( int j = 1;j <= m;j ++ )if( j > 1 and c[i][j] ^ c[i][j - 1] )if( h[i][j] > 1 ) l[i][j] = max( l[i - 1][j], l[i][j - 1] );else l[i][j] = l[i][j - 1];else l[i][j] = j; for( int i = 1;i <= m;i ++ ) r[0][i] = m; for( int i = 1;i <= n;i ++ )for( int j = m;j;j -- )if( j < m and c[i][j] ^ c[i][j + 1] )if( h[i][j] > 1 ) r[i][j] = min( r[i - 1][j], r[i][j + 1] );else r[i][j] = r[i][j + 1];else r[i][j] = j;

你過早的刻畫了左右邊界,就會(huì)出現(xiàn)上面這種。雖然對(duì)于紫紅色點(diǎn)而言確實(shí)是這個(gè)矩形。

但對(duì)于后面的黃色點(diǎn)而言,其遞推的左邊界也被限制在了黑線處,就算不到紅色矩形了。

#include <bits/stdc++.h> using namespace std; #define maxn 2005 int n, m; int c[maxn][maxn], h[maxn][maxn], l[maxn][maxn], r[maxn][maxn];int main() {scanf( "%d %d", &n, &m );for( int i = 1;i <= n;i ++ )for( int j = 1;j <= m;j ++ )scanf( "%d", &c[i][j] );for( int i = 1;i <= n;i ++ )for( int j = 1;j <= m;j ++ )if( i > 1 and c[i][j] ^ c[i - 1][j] ) h[i][j] = h[i - 1][j] + 1;else h[i][j] = 1;for( int i = 1;i <= n;i ++ )for( int j = 1;j <= m;j ++ )if( j > 1 and c[i][j] ^ c[i][j - 1] ) l[i][j] = l[i][j - 1];else l[i][j] = j;for( int i = 1;i <= n;i ++ )for( int j = m;j >= 1;j -- )if( j < m and c[i][j] ^ c[i][j + 1] ) r[i][j] = r[i][j + 1];else r[i][j] = j;int ans1 = 0, ans2 = 0;for( int i = 1;i <= n;i ++ )for( int j = 1;j <= m;j ++ ) {if( i > 1 and c[i][j] ^ c[i - 1][j] ) {l[i][j] = max( l[i][j], l[i - 1][j] );r[i][j] = min( r[i][j], r[i - 1][j] );}ans1 = max( ans1, min( r[i][j] - l[i][j] + 1, h[i][j] ) * min( r[i][j] - l[i][j] + 1, h[i][j] ) );ans2 = max( ans2, ( r[i][j] - l[i][j] + 1 ) * h[i][j] );}printf( "%d\n%d\n", ans1, ans2 );return 0; }

總結(jié)

以上是生活随笔為你收集整理的[ZJOI2007] 棋盘制作(单调栈 / DP悬线法)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。