题目:
有一个Boolean组成的矩阵,大小是N*M,代表路面,能不能在上面行走,假设胖子的形状都是正方形,也就是s*s的矩阵,其中s代表胖子的边长,现在想寻找从矩阵最左上角能走到矩阵最右下角的最大腰围的胖子。而胖子只能往右边和往下面两个方向移动,且每次只能移动一步。要求每次胖子所占用的地面都是可以在上面行走的,也就是都是值为true的元素。
分析:
举个栗子,有以下的矩阵,(其中绿色代表的是true,红色代表的是false)
此图中,最大的胖子的s可以达到2,按照如下图中的路线行走,(蓝色虚线代表最大的胖子,黄线代表走的路线)
所以应该返回2。
如果是一个4*5的矩阵,并且里面都是true,那么应该返回4。
如果最小为1边长的胖子也不能从左上角移动到右下角,那么返回0。
怎么思考这样的问题,路径的遍历可以用DFS,那么又如何找到最大的胖子的边长呢?
一种比较暴力的方法就是遍历从s = 1到min(N, M),然后对s的边长进行DFS看是否能顺利完成路径,如果成功则记录,如果失败则停止,返回最后一个成功的s就是最大的边长。
但是这样太费时了。
我的想法是:通过把问题分解成两个子问题,通过递归去解决。
怎么分解?题目中只能往右或者往下走。
举个栗子,
如果往右走,它的最大的胖子的大小被两个因素限制:第0列最大的连续true的数量mmax,以及子问题除去第0列剩下的矩阵的最大胖子rs
往右走的最大胖子right_s = min(mmax, rs)
如果往左走也是类似:
它的最大的胖子的大小被两个因素限制:第0行最大的连续true的数量nmax,以及子问题除去第0列剩下的矩阵的最大胖子bs
往右走的最大胖子bottom_s = min(nmax, bs)
而最终的胖子的大小是s = max(right_s, bottom_s),即从往下走和往右走中选出最大值。
让问题解函数为getMax(A, x, y, n, m),其中A表示矩阵,x,y表示胖子刚开始待的位置,n,m表示矩阵大小。
那么问题分解的过程如下图:
这是非常典型的分解问题+递归解决。
而终结的条件就是当胖子的右下角到达了n和m的边界的时候,进行判断是否胖子当前所占的地方是否全为true,因为胖子来的路中左边和右边我们都通过最左列和最上行的true最大的数量判断过,不过胖子最后占的位置我们还没有判断,如果都为true,则当前胖子的长度s就是答案,否则s还要小。
代码如下:
#include<stdio.h>
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;
int getMax(vector<vector<bool>>&A, int x, int y, int n, int m){
int nmax = 0, mmax = 0;
if (x >= n || y >= m)return 0;//终结问题的部分,已到了边界
while (nmax + x< n && A[y][nmax + x] == true)//找出最多的列的T
nmax++;
while (mmax + y< m && A[mmax + y][x] == true)//找出最多的行的T
mmax++;
bool flag = false;
if (nmax == 0 && mmax == 0)//终结问题的部分
return 0;
if (nmax == mmax && nmax + x == n && mmax + y == m){//终结问题的部分,到了边界,判断是不是都是true
flag = true;
for (int i = x; i < n; ++i){
for (int j = y; j < m; ++j){
if (A[j][i] == false){
flag = false;
break;
}
}
}
}
if (flag == true){
return nmax;
}
int ret;
if (nmax == 0)
ret = min(mmax, getMax(A, x, y + 1, n, m));
else if (mmax == 0)
ret = min(nmax, getMax(A, x + 1, y, n, m));
else
ret = max(min(mmax, getMax(A, x + 1, y, n, m)), min(nmax, getMax(A, x, y + 1, n, m)));//子问题1和子问题2
return ret;
}
int solution(vector<vector<bool>> &A){
int m = A.size();//找出行数
int n = A[0].size();//找出列数
int ret = getMax(A, 0, 0, n, m);//进入递归
return ret;
}
int main(){
vector<vector<bool>>VV;
//VV = { { true, true, true, false }, { true, true, true, false }, { true, true, true, false }, { true, true, true, true }, { false, true, true, true } };//答案是2
//VV = { { true } };//答案是1
//VV = { { true, true, false, false }, { true, false, false, false }, { false, true, false, true }, { false, true, true, true } };//答案是0
//VV = { { true,true,true }, { true,true,true }, { true,true,true } };//答案是3
VV = { { true, true, true, true }, { true, true, true, true }, { false, true, true, true } };//答案是2
cout << solution(VV) << endl;
return 0;
}