题目描述
给你一个边长为 a 的六边形 01 矩阵,请找到一个最大的全1子三角形,输出三角形的边长b。
方法一:动态规划方法
求解思路
对于本题目要求解最大的全1子三角形,因为题目给出的数据,为了方便题目的求解,我们将一维数组表示的六边形矩阵装入二维矩阵。根据边长为a的六边形矩阵,共有2a行,最长4a-1列。我们将其元素放在矩形矩阵的[a-i-1,3a+i-1]中,并且我们根据正六边形的对称性,我们选定其一个朝向,并且用dp[i][j]记录以矩形矩阵中某一个点为顶点的三角形边长的最大值。根据图形的特点我们有状态转移方程:dp[i][j]=min{dp[i+1][j-1],dp[i+1][j+1]}+1.最后根据上述的思路即可得到本题的答案。
解题代码
class Solution { public: int largestSubTriangle(int a, vector<int>& maps) { int n = 4 * a - 1; vector<vector<int> > matrix(2 * a, vector<int>(n, 0)); // 覆盖六边形的矩阵 vector<vector<int> > tri(2 * a, vector<int>(n, 0)); // 三角形方向 vector<vector<int> > dp(2 * a, vector<int>(n, 0)); int hycount = 0; int hyres = 0; for(int i = 0, k = 1; i < a; i++) { //遍历上半矩阵 for(int j = a - i - 1; j < 3 * a + i; j++) { matrix[i][j] = maps[hycount++]; tri[i][j] = k % 2; // 上半个六边形每行奇数朝上 k++; } k = 1; } for(int i = a - 1, k = 0; i >= 0; i--) { //遍历下半矩阵 for(int j = a - 1 - i; j < 3 * a + i; j++) { matrix[2 * a - i - 1][j] = maps[hycount++]; tri[2 * a - i - 1][j] = k % 2; // 下半个六边形每行偶数朝上 k++; } k = 0; } for(int i = 2 * a - 1; i >= 0; i--) { //找朝上的最大三角形 for(int j = n - 1; j >= 0; j--) { if(matrix[i][j] == 1) { dp[i][j] = 1; if(i < 2 * a - 1 && j < n - 1 && j > 0 && tri[i][j] == 1 && dp[i + 1][j] == 1) dp[i][j] = min(dp[i + 1][j - 1], dp[i + 1][j + 1]) + 1; hyres = max(hyres, dp[i][j]); // 维护最大值 } } } for(int i = 0; i < 2 * a; i++) { //找朝下的最大三角形 for(int j = 0; j < n; j++) { if(matrix[i][j] == 1) { dp[i][j] = 1; //除去最上一行、最左右两边、朝向向上、上方一个不为1,这四种情况不能为顶部三角,继续添加 if(i > 0 && j < n - 1 && j > 0 && tri[i][j] == 0 && dp[i - 1][j] == 1) dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j + 1]) + 1; hyres = max(hyres, dp[i][j]); // 更新最大值 } } } return hyres; // 返回结果 } };
复杂度分析
时间复杂度:遍历矩形矩阵,两层循环,因此时间复杂度为( )
空间复杂度:引入辅助二维数组,因此空间复杂度为( )
方法二:基于动态规划的优化方法
求解思路
对于方法一,我们考虑在不引入多余的二维数组的情况下对问题进行求解。对于矩形矩阵,我们对于上半矩阵的第i行,那么该行六边形共有2*(a+i)+1个元素,同理下半行也是如此。因此我们设置一个index直接访问数组,不用像方法一中使用二维矩阵来存储。综上,其余根据方法一的做法,即可求得本题的答案。
解题代码
class Solution { public: // int largestSubUpperTriangle(int a, vector<int> &maps) { //找到最大的朝下的三角形 int index = 0; // 引入index对本题的解法进行优化 int hyres = 0; // 记录结果 for (int i = 0; i < a; i++) { //遍历上半个六边形 int n = 2 * (a + i) + 1; // 该行长度 for (int j = 0; j < n; j++) { if (j % 2 == 1) { //只找朝下的小三角形 if (j > 1 && j < n - 2 && i != 0) // 不在边界 maps[index] = (maps[index] && maps[index - n + 1] ? 1 + min(maps[index - n + 2], maps[index - n]) : maps[index]); hyres = max(hyres, maps[index]); } index++; } } for (int i = a - 1; i >= 0; i--) { //遍历下半个六边形 int n = 2 * (a + i) + 1; // 该行长度 for (int j = 0; j < n; j++) { if (j % 2 == 0) { //只找朝下的小三角形 if (i == a - 1) { //两种情况,与方法一这里对应下半个矩阵从a-1开始 if (j > 0 && j < n - 1) // 不在边界 maps[index] = (maps[index] && maps[index - n] ? 1 + min(maps[index - n - 1], maps[index - n + 1]) : maps[index]); } else { maps[index] = (maps[index] && maps[index - n - 1] ? 1 + min(maps[index - n - 2], maps[index - n]) : maps[index]); } hyres = max(hyres, maps[index]); } index++; } } return hyres; } int largestSubTriangle(int a, vector<int>& maps) { int hyres = 0; // 存储结果 hyres = max(hyres, largestSubUpperTriangle(a, maps)); // 朝下的三角形 reverse(maps.begin(), maps.end()); // 逆转数据 hyres = max(hyres, largestSubUpperTriangle(a, maps)); // 朝上的三角形 return hyres; // 返回结果 } };
复杂度分析
时间复杂度:两层循环,因此时间复杂度为
空间复杂度:没有引入额外的内存地址空间,因此空间复杂度为