給定乙個n*m的01矩陣,尋找最大的,相鄰值不相同的子矩陣與子方陣。
\(n,m\leq 2,000\)
樸素的做法是列舉兩個點來確定矩陣,再暴力判斷能不能滿足條件。
複雜度\(o(n^4)\)**。考慮優化:判斷能不能滿足條件時,存在很多重複判斷,可以預處理。
但是,怎樣進行預處理呢?這就需要我們改變列舉方式,簡化預處理的步驟。
今年應該不考
是一種dp(?)演算法,常用於求滿足條件的矩陣。
如果將一條線當做1*k的矩陣看待,則我們列舉每一條滿足條件的線,並嘗試將其向左右拓展構造矩陣。
那麼只要在左右拓展長度中分別取min,就是所求答案。這種方法的複雜度是多少?
每個點如果和上方不同,就可以接到上面的線中。所以列舉每個點,就可以遞推求出懸線up
\[up_=up_+(b_\not= b_)
\]其中b是原始矩陣,初始值是1。複雜度\(o(n^2)\)
左右端拓展的物件是單個點,所以對於每個點都預處理出能向左右拓展到的位置。用遞推求出。
\[\beginb_\not=b_\quad left_=j\\b_=b_\quad left_=left_\end
\]\[\beginb_\not=b_\quad right_=j\\b_=b_\quad right_=right_\end
\]複雜度仍然\(o(n^2)\)
2k可過。
我們需要證明的是,懸線法可以列舉到所有矩形。
假設我們要在這一團滿足條件的聯通塊中找矩形。(醜,真tm醜)
那麼它會不會取藍色呢?不會,很明顯綠色比他更優。有乙個性質:矩形邊緣必然緊貼聯通塊邊緣。
所以對於每條懸線,它取到的矩陣是盡量大的。
(其實綠色也不滿足,因為左右端沒有取完整。)
畫圖寶才,luogu撿到鬼了
由剛才的性質得出,這個矩形所在的聯通塊即便邊緣再粗糙,總是會有一條懸線的上端和矩形的上端重合。
所以,只要每條懸線都列舉到,每個可能是答案的矩形就會列舉到,而且會讓它們盡量大。這題就解決了。
別看了 抄的
#include//iostream庫有left函式,會衝突
#includeusing namespace std;
const int maxn=2005;
int n,m;
bool b[maxn][maxn];
int left[maxn][maxn],right[maxn][maxn],up[maxn][maxn];
int sq,rect,temp;
int main()
temp=(right[i][j]-left[i][j]+1);
rect=max(rect,up[i][j]*temp);
sq=max(sq,min(up[i][j],temp)*min(up[i][j],temp));
//正方形同理。
} printf("%d\n%d\n",sq,rect);
return 0;
}
最後,祝大家身體健康,再見。 洛谷 P1169 ZJOI2007 棋盤製作
西洋棋是世界上最古老的博弈遊戲之一,和中國的圍棋 象棋以及日本的將棋同享盛名。據說西洋棋起源於易經的思想,棋盤是乙個8 times 88 8大小的黑白相間的方陣,對應八八六十四卦,黑白對應陰陽。而我們的主人公小q,正是西洋棋的狂熱愛好者。作為乙個頂尖高手,他已不滿足於普通的棋盤與規則,於是他跟他的好...
P1169 ZJOI2007 棋盤製作
隨手一寫就衝進了最優解的第一頁?本來以為是dp,但是經過仔細分析.這不就是二進位制 單調棧麼?然後想正方形的情況.emm.好像正方形一定是最大矩形的子矩陣吧 聽說此題dp也可行?include include include include include using namespace std t...
P1169 ZJOI2007 棋盤製作 貪心
乙個矩陣中求乙個最大的子矩陣和子正方形使得它們其中都是01交錯。l ef ti,j left lefti,j 表示 i,j i,j i,j 往左擴充套件多遠,rig hti,jright righti j 表示 i,j i,j i,j 往右擴充套件多遠,upi j up up i,j 表示 i,j ...