看到这道题有些人很容易放弃,其实这道题不是很难,主要是题目长,读的容易让人放弃,但是
只要抓住一些性质就可以解决该问题。
本题中的定义放到图像里其实就是个金字塔,下层的那部分比上一层的那部分,长度加2,
并且该层那个长度区间内都是1才行。是个金字塔形状里都是1就行。
我们暴力的解法是什么呢?,其实就是遍历整个数组,以每个数组下标为金子塔尖,往下去统计有多少个金字塔,那么这个的时间复杂度是1e8,会超时,所以我们得想别的方法去优化统计多少个金字塔这部分方面去想。那么我们再深入思考一下,我们每次遍历的时候,每次都要向下去统计金字塔,那在统计的时候,我们每次都要关注当前外层是否从上一层外层下来,统计好之后我们怎么想呢?目前已经知道我们的行数是确定的,那么其实只要他的下面的外层可以延伸到金字塔尖的话,我们就可以根据行数和外层可以延伸的长度那么我们就可以知道金字塔数量了,
求每个塔尖的话都是可以这样的,那么外层可以延伸的长度和行数我们怎么确定呢?以正金字塔为例子,一是预处理出来,但是这样复杂度还是比较高,那么我们是不是可以想一下从下往上遍历的话,我们是不是就可以固定金字塔的高度了,比如当前点是i,j,那可以从i + 2到i + 1,i + 1到i。那么高度我们已经确定好了,怎么去确定可以延伸的长度呢?
这个确实不好想,我们目前推出来的只知道每一次长度相差为2,只知道判断从左下角上来,
从右下角上来,但是不知道他中间那段能不能上来,现在就是要确定中间那段如何让他上来,
但是我们仔细想想的话,根据前面的推导,我们在向上推的话,我们是不是只能推导到金字塔尖,那么如果不是金子塔尖呢?我们该怎么去推导呢?其实到这里我们就已经知道了,这是一个动态规划问题,那么动态规划问题的本质是什么呢?找子问题,那么我们是不是可以想一下,最顶上的金子塔尖,是不是可以从下层的金字塔尖推上去,是可以的,我们只用知道左下角,下方,右下角上来就知道了,去个min就可以了,那么状态转移方程就是
f[i][j] = min(f[i + 1][j],min(f[i + 1][j + 1],f[i + 1][j - 1])) + 1;
class Solution {
public:
int f[1100][1100];
int countPyramids(vector<vector<int>>& grid)
{
memset(f,0,sizeof(f));
int n = grid.size(),m = grid[0].size();
//i + 1,j
//i + 1,j - 1
//i + 1,j + 1
int ans = 0;
for(int i = n - 1;i >= 0;i--)
{
for(int j = 0;j < m;j++)
{
if(grid[i][j] == 0) continue;
if(j - 1 < 0 || j + 1 >= m || i + 1 >= n) continue;
if(grid[i + 1][j] == 0 || grid[i + 1][j - 1] == 0 || grid[i + 1][j + 1] == 0) continue;
f[i][j] = min(f[i + 1][j],min(f[i + 1][j + 1],f[i + 1][j - 1])) + 1;
ans += f[i][j];
}
}
//i - 1,j
//i - 1,j - 1
//i - 1,j + 1
memset(f,0,sizeof(f));
for(int i = 0;i < n;i++)
{
for(int j = 0;j < m;j++)
{
if(grid[i][j] == 0) continue;
if(i - 1 < 0 || j + 1 >= m || j - 1 < 0) continue;
if(grid[i - 1][j] == 0 || grid[i - 1][j - 1] == 0 || grid[i - 1][j + 1] == 0) continue;
f[i][j] = min(f[i - 1][j],min(f[i - 1][j + 1],f[i - 1][j - 1])) + 1;
ans += f[i][j];
}
}
return ans;
}
};