luogu1736:創意喫魚法:棋盤DP

題目連接

  • 該題是luogu試煉場的2-17:T6

題目大意

  1. n*m的0,1棋盤中;
  2. 找最大的正方形,要求:(左或者右)對角線上全是1,其他位置全是0。

題目分析

  • 1387最大子正方形十分類似,但要增加預處理。
  • 因爲左右對角線都有可能是最值,所以分開兩次DP:一次左斜,一次右斜,再取最值。


解題思路:左斜

問什麼設什麼: f[i][j]表示以(i,j)爲右下角的正方形,符合條件時的邊長;

  • 如果a[i][j]是0,則f[i][j]一定是0;
  • 如果a[i][j]是1,則需要考慮左上角,左邊,上邊:
  1. f[i][j]一定與f[i-1][j-1]的值有關,
  2. 第i行前面的空格,第j列上面的空格情況;
    在這裏插入圖片描述
  3. 預處理出橙色第i行部分的值:ax[i][j]表示,從(i,j)往左,有多少個空的格子,不包含(i,j);
  4. 預處理出橙色第j列部分的值:ay[i][j]表示,從(i,j)往上,有多少個空的格子,不包含(i,j);
左邊的搞好了,對稱搞一次右邊的,再求一次最值。

參考代碼
//luogu1736:創意喫魚法
//最大子正方形的對角線
//左斜和右斜各做一次
 
#include<bits/stdc++.h>
using namespace std;
#define ma 2505

int ax[ma][ma],ay[ma][ma];
int f[ma][ma],a[ma][ma];
int n,m,ans=0;
int main()
{
	cin>>n>>m;
	
	//左斜:從左上到右下 
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			scanf("%d",&a[i][j]);
			if(a[i][j]==0)//預處理 
			{
				ax[i][j]=ax[i][j-1]+1;
				ay[i][j]=ay[i-1][j]+1;
			}
			else if(a[i][j]==1)
			{
				f[i][j]=min(f[i-1][j-1],min(ax[i][j-1],ay[i-1][j]))+1;
				ans=max(f[i][j],ans);
			}
		}
	}	
	//右斜:從右上到左下 
	memset(f,0,sizeof(f));
	memset(ax,0,sizeof(ax));
	memset(ay,0,sizeof(ay));
	for(int i=1;i<=n;i++)
	{
		for(int j=m;j>=1;j--)
		{
			if(a[i][j]==0)
			{
				ax[i][j]=ax[i][j+1]+1;
				ay[i][j]=ay[i-1][j]+1;
			}
			else if(a[i][j]==1)
			{
				f[i][j]=min(f[i-1][j+1],min(ax[i][j+1],ay[i-1][j]))+1;
				ans=max(f[i][j],ans);
			}
		}
	}
	
	cout<<ans<<endl;
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章