1. 程式人生 > >刷題記錄【ZJOI2007棋盤製作】二維DP,懸線法。。。

刷題記錄【ZJOI2007棋盤製作】二維DP,懸線法。。。

https://www.luogu.org/problemnew/show/P1169
題目描述
國際象棋是世界上最古老的博弈遊戲之一,和中國的圍棋、象棋以及日本的將棋同享盛名。據說國際象棋起源於易經的思想,棋盤是一個 8 × 8 8×8 大小的黑白相間的方陣,對應八八六十四卦,黑白對應陰陽。
而我們的主人公小Q,正是國際象棋的狂熱愛好者。作為一個頂尖高手,他已不滿足於普通的棋盤與規則,於是他跟他的好朋友小W決定將棋盤擴大以適應他們的新規則。
小Q找到了一張由 N

× M N \times M 個正方形的格子組成的矩形紙片,每個格子被塗有黑白兩種顏色之一。小Q想在這種紙中裁減一部分作為新棋盤,當然,他希望這個棋盤儘可能的大。
不過小Q還沒有決定是找一個正方形的棋盤還是一個矩形的棋盤(當然,不管哪種,棋盤必須都黑白相間,即相鄰的格子不同色),所以他希望可以找到最大的正方形棋盤面積和最大的矩形棋盤面積,從而決定哪個更好一些。
於是小Q找到了即將參加全國資訊學競賽的你,你能幫助他麼?
輸入輸出格式
輸入格式:

包含兩個整數N和M,分別表示矩形紙片的長和寬。接下來的N行包含一個N ×M的01矩陣,表示這張矩形紙片的顏色(0表示白色,1表示黑色)。
輸出格式:

包含兩行,每行包含一個整數。第一行為可以找到的最大正方形棋盤的面積,第二行為可以找到的最大矩形棋盤的面積(注意正方形和矩形是可以相交或者包含的)。
第一次接觸這種型別的二維dp
因為過於水不得不在閱讀過下面這篇論文之後才AC掉
https://blog.csdn.net/clover_hxy/article/details/50532289
自閉了
程式碼

#include <iostream>
#include <cstdio>
#include <cstdlib> #include <algorithm> using namespace std; #define N 2005 #define ll long long #define right wrong #define left niconico int f[N][N],height[N][N],left[N][N],right[N][N],n,m,ans1,ans2,map[N][N]; inline int p2(int x){return x*x;} int main() { scanf("%d%d",&n,&m); for(int i = 1 ; i <= n ; i ++) { for(int j = 1 ; j <= m ; j ++) { scanf("%d",&map[i][j]); left[i][j]=right[i][j]=j; height[i][j]=1; } } for(int i = 0; i <= n; i ++ )map[i][0]=-1; for(int j = 1; j <= m; j ++ )map[0][j]=-1; for(int i = 1; i <= n ; i ++) for(int j = 1; j <= m ; j ++) { if(map[i][j]+map[i][j-1]==1)left[i][j]=left[i][j-1]; } for(int i = 1; i <= n ; i ++) for(int j = m; j >= 1 ; j --) { if(map[i][j]+map[i][j-1]==1)right[i][j-1]=right[i][j]; } for(int i = 1 ; i <= n; i ++) for(int j = 1 ; j <= m; j ++) { if(map[i][j]+map[i-1][j]==1) { height[i][j]=height[i-1][j]+1; left[i][j]=max(left[i][j],left[i-1][j]); right[i][j]=min(right[i][j],right[i-1][j]); } } for(int i = 1; i <= n ; i ++) for(int j = 1 ; j <= m ; j ++) { ans1 = max(ans1,height[i][j]*(right[i][j]-left[i][j]+1)); ans2 = max(ans2,p2(min(height[i][j],right[i][j]-left[i][j]+1))); } printf("%d\n%d",ans2,ans1); }

最後想一想還是寫一下思路吧
首先,懸線法的本質是列舉每一個點為下端點的最大懸線,將懸線左右移動,所能移動的最長區間長度即為最大矩形的一邊長。而懸線的長即為另一邊長。
由於懸線移動的時間複雜度為 O ( n ) O(n) ,所以不滿足本題要求。為了能在O(1)時間內求出最大區間長,我們可以採用預處理的方法。
用陣列left[i][j],right[i][j]表示從i,j開始最靠左、右的符合要求區間終點。
則先遞推求出長度為1的懸線對應的left[i][j],right[i][j],
再按照 l e f t [ i ] [ j ] = m a x ( l e f t [ i ] [ j ] , l e f t [ i 1 ] [ j ] ) left[i][j]=max(left[i][j],left[i-1][j]) r i g h t [ i ] [ j ] = m i n ( r i g h t [ i ] [ j ] , r i g h t [ i 1 ] [ j ] ) right[i][j]=min(right[i][j],right[i-1][j])
進行遞推
最後列舉所有的i,j
則I,j對應的矩形面積為 h e i g h t [ i ] [ j ] × ( r i g h t [ i ] [ j ] l e f t [ i ] [ j ] + 1 ) height[i][j]\times(right[i][j]-left[i][j]+1) 正方形面積為
m i n ( h e i g h t [ i ] [ j ] , r i g h t [ i ] [ j ] l e f t [ i ] [ j ] + 1 ) 2 min(height[i][j],right[i][j]-left[i][j]+1)^2