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;
}

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