洛谷P1169 [ZJOI2007]棋盤製作

題目描述

國際象棋是世界上最古老的博弈遊戲之一,和中國的圍棋、象棋以及日本的將棋同享盛名。據說國際象棋起源於易經的思想,棋盤是一個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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章