題目描述
國際象棋是世界上最古老的博弈遊戲之一,和中國的圍棋、象棋以及日本的將棋同享盛名。據說國際象棋起源於易經的思想,棋盤是一個8*8大小的黑白相間的方陣,對應八八六十四卦,黑白對應陰陽。
而我們的主人公小Q,正是國際象棋的狂熱愛好者。作爲一個頂尖高手,他已不滿足於普通的棋盤與規則,於是他跟他的好朋友小W決定將棋盤擴大以適應他們的新規則。
小Q找到了一張由N*M個正方形的格子組成的矩形紙片,每個格子被塗有黑白兩種顏色之一。小Q想在這種紙中裁減一部分作爲新棋盤,當然,他希望這個棋盤儘可能的大。
不過小Q還沒有決定是找一個正方形的棋盤還是一個矩形的棋盤(當然,不管哪種,棋盤必須都黑白相間,即相鄰的格子不同色),所以他希望可以找到最大的正方形棋盤面積和最大的矩形棋盤面積,從而決定哪個更好一些。
於是小Q找到了即將參加全國信息學競賽的你,你能幫助他麼?
輸入輸出格式
輸入格式:
包含兩個整數N和M,分別表示矩形紙片的長和寬。接下來的N行包含一個N * M的01矩陣,表示這張矩形紙片的顏色(0表示白色,1表示黑色)。
輸出格式:
包含兩行,每行包含一個整數。第一行爲可以找到的最大正方形棋盤的面積,第二行爲可以找到的最大矩形棋盤的面積(注意正方形和矩形是可以相交或者包含的)。
輸入輸出樣例
輸入樣例#1:
3 3
1 0 1
0 1 0
1 0 0
輸出樣例#1:
4
6
說明
對於20%的數據,N, M ≤ 80
對於40%的數據,N, M ≤ 400
對於100%的數據,N, M ≤ 2000
這道題標籤是DP(我也是抱着刷dp的心態刷的這題),但是我看完題面之後卻想不出dp咋寫,反倒是騷操作O(n^2logn)卡過了。。。。。
首先對於一個可行的子矩陣,有兩種可能性,第一個是行數加列數加1/0爲奇數的棋盤 另一種則是偶數。
那麼我們就可以對着兩種情況合理枚舉,我們枚舉子矩陣的左邊界,然後對於這個邊界尋找最大矩形以及正方形。具體的方案是先用二維並查集維護每一列連續的區間(如圖上橫向的矩形就是一個個集合),然後我們如果知道對於一個矩形的長度,上下可以延伸幾個格,也就是說如果i上方2格之後,該格子的長度小於了i的長度,那麼如以i長度爲矩形一邊長往上可擴展的距離就是2 即l[i]=2,同理可得r[i]。如何O(n)枚舉參照物i,更新答案。那麼最關鍵的部分來了——如何求l r數組呢?我們可以先考慮,如果x是最長的,且無和x同長的矩形(a[i].w),那麼l[i]=i-1,r[i]=i+1。所以我們就可以先排序,然後通過雙向鏈表的刪除操作維護該序列,這樣每次刪掉最大的,就能依次處理出所有l r。但是如果相鄰兩個元素相等呢(此處相鄰是指鏈表中相鄰)?隊列。把相等的入隊列,直到不等,全部彈出,時間複雜度O(2n)所以完全沒問題。這個樣子就可以O(n^2*大常數*logn)卡過了(^.^)!
圖
上代碼!
#include<bits/stdc++.h>
using namespace std;
#define MN 2050
int n,m,b[MN],fa[MN][MN],ha[MN][MN],lk1[MN],lk2[MN],l[MN],r[MN],ans=-2100000000,ans2=-2100000000;
struct lll{
int w,pos;
}a[MN];
bool cmp(lll a,lll b)
{
return a.w>b.w;
}
int find(int x,int y)
{
return fa[x][y]==y?y:fa[x][y]=find(x,fa[x][y]);
}
void work(int _)
{
for(int i=1;i<=n;i++) for(int j=1;j<=m+1;j++) fa[i][j]=j;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if((ha[i][j]+i+j)%2==_ )
fa[i][find(i,j)]=find(i,j+1);
}
for(int j=1;j<=m;j++)
{
for(int i=1;i<=n;i++)
{
b[i]=a[i].w=find(i,j)-j;
a[i].pos=i;lk1[i]=i+1;lk2[i]=i-1;
}
sort(a+1,a+1+n,cmp);
for(int k=1;k<=n;k++)
{
int i=a[k].pos;
l[i]=lk2[i];r[i]=lk1[i];
lk1[lk2[i]]=lk1[i];
lk2[lk1[i]]=lk2[i];
}
for(int i=1;i<=n;i++)
{
ans=max(ans,b[i]*(r[i]-l[i]-1));
ans2=max(ans2,min(b[i]*b[i],(r[i]-l[i]-1)*(r[i]-l[i]-1)));
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>ha[i][j];
work(1);work(0);
cout<<ans2<<endl<<ans;
return 0;
}