一、前言
今天依然是动态规划的题,老实说,今天的题目不是特别难,但是按照安排,还是照例写一份博客督促自己完成每日任务
二、题目描述
三、题目分析
这个题目最初的时候,我打算从最中心处看起,逐渐向外扩散,但是后来发现我想的简单了,经过一番折腾,我尽可能的将思路清晰的讲出来。
首先,因为是动态规划专题,所以重点在于状态转移方程的构建,那么就需要明确dp的含义,如果dp是一个二维数组dp[i][j],那么如何表示相邻正方形的关系呢?如果dp给定的是一个正方形,那么起码需要两个点,也就是四个xy坐标,那么至少需要一个四维数组并且关系式还难以给出。所以更好的方法是找出矩阵中一给定点那一行和那一列的连续1的数目,因为只有连续的一才可能满足条件。那么问题又来了,一行和一列的连续1的数目可能不同,怎么存在二维数组呢?那就开辟一个三维数组就是了,第三维的长度为2,分别表示 第i行和第j列。
问题好像解决的差不多了,但是还有个更严重的,如果dp[i][j][0]指的是那一行连续1的个数,dp[i][j-1][0]指的也是那一行连续1的个数,就没有区别了!!所以我们将dp[i][j][0]的含义改为在(i,j)这个点左侧(包括自身)中连续1的数目,dp[i][j][1]的含义为该点上面连续1的个数。
这样状态转移方程就容易得出了
dp[i][j][0]=dp[i][j-1][0]+1;
dp[i][j][1]=dp[i-1][j][1]+1;
好的,当我们创建一个dp数组之后,通过状态转移方程的遍历,将所有dp的值都修改一番之后就到了下一步。
(图非原创,侵删)
下一步就是,先规定一个最大边的值为0。然后遍历dp数组,获取每个点的dp[i][j][0]和dp[i][j][1],取得其中的小值(因为如果该点左侧有五个连续1,上侧有三个连续1,那么正方形最大边长最多为3),记为currentside。每次遍历时都要进行一次判断,如果maxside大于currentside,那么进入到下个点的判断,否则,就要判断正方形的左边和上边是否满足条件(因为下边和右边已经满足),
右下角为(i,j),边长为currentside,那么最左的那条边的最低点就是(i,j-currentside+1),只需要判断它的dp[i][j-currentside+1][1]是否大于等于3即可,同理,上面的那条边也可以这样计算。如果满足条件,那么更新maxside,否则就要将currentside减一,继续进入循环直到满足条件。
四、代码
public int largest1BorderedSquare(int[][] grid) {
int len_index=grid.length;//行数
int len_column=grid[0].length;//列数
int [][][]dp=new int [len_index+1][len_column+1][2];
for(int i=1;i<=len_index;i++)//给dp重新赋值
for(int j=1;j<=len_column;j++)
{
if(grid[i-1][j-1]==0)
continue;
dp[i][j][0]=dp[i][j-1][0]+1;
dp[i][j][1]=dp[i-1][j][1]+1;
}
int maxside=0;
for(int i=1;i<=len_index;i++)
for(int j=1;j<=len_column;j++)
{
int currentside=Math.min(dp[i][j][0],dp[i][j][1]);
if(maxside>=currentside)//满足条件则继续计算
continue;
for(;currentside>maxside;currentside--)
{
if (dp[i][j - currentside + 1][1] >= currentside && dp[i - currentside + 1][j][0] >= currentside) {
maxside = currentside;
break;
}
}
}
return maxside*maxside;
}
五、感言
动态规划做了也有十多题了,但是大多还是不能完全独立做出来!郁闷郁闷!!!
flag不倒,坚持每日一题!!