最大子矩阵问题
- 格式:docx
- 大小:17.89 KB
- 文档页数:3
最大子矩阵问题最大子矩阵问题最优子矩阵是建立在数列连续最大和的基础上的。
所谓最优子矩阵,就是指在一个n*m二维的矩阵中,确定一个小的矩阵,使这个小矩阵中所有元素的和最大。
思考一下最优子矩阵和连续最大和的异同:1、所求的和都具有连续性;2、连续最大和是一维问题,最优子矩阵是二维问题另外,对于一个矩阵而言,如果我们将连续k行的元素纵向相加,并对相加后所得的数列求连续最大和,则此连续最大和就是一个行数为k的最优子矩阵!由此,我们可以将二维的矩阵压缩成一维矩阵,转换为线性问题,从而求解。
其实就是将二维的问题,换成一维#include#include#include#define N 4 //matrix N*Nint getrand()int num = rand()%11-5;return num;void print_matrix(int A[N][N])for(i=0;ifor(j=0;jprintf("%d\t",A[i][j]);printf("\n");int max_sub_array(int B[], int n)int i = 0;int sum = 0;int max = 0;sum += B[i];if(sum>max)max = sum;else if(sumsum = 0;return max;int max_sub_matrix(int A[N][N])int last_i = 0;int last_j = 0;int max = 0;int tmp = 0;int array[N];/*i=0,表示包含第一行的最大子矩阵 i=1...类推*/ for(i=0;ifor(k=0;karray[k] = 0;for(j=i;jfor(k=0;karray[k] += A[j][k];tmp = max_sub_array(array, N);if(tmp>max)last_i = i;last_j = j;max = tmp;/*最大子矩阵开始和结束的行*/printf("last_i is %d, last_j is %d\n",last_i+1, last_j+1); return max;int main(int argc, char *argv[])int ret;int A[N][N];srand((unsigned int)time(NULL));for(i=0;ifor(j=0;jA[i][j] = getrand();print_matrix(A);ret = max_sub_matrix(A);printf("max is %d\n",ret);system("PAUSE");return 0;。
如何应用分治算法求解问题分治算法,英文名为Divide and Conquer Algorithm,是一种高效的算法设计策略,在计算机科学中有着广泛的应用。
该算法将一个大问题分解成多个小问题,各自独立地解决,再将结果合并起来得到最终结果。
在本文中,我们将阐述如何应用分治算法求解问题,并通过几个实例来具体说明该算法的应用。
一、分治算法的原理分治算法的核心思想是将一个大问题分解成若干个小问题来解决,然后将这些小问题的解组合起来生成大问题的解。
其具体步骤如下:1. 分解:将原问题划分成若干个规模较小的子问题。
2. 解决:递归地解决每个子问题。
如果子问题足够小,则直接求解。
3. 合并:将所有子问题的解合并成原问题的解。
分治算法的主要优点在于它可以有效地缩小问题规模,从而缩短整个算法的执行时间。
另外,该算法天然适用于并行计算,因为每个子问题都是独立求解的。
二、分治算法的应用分治算法在各种领域都有广泛应用,包括数学、自然科学、计算机科学等。
以计算机科学领域为例,分治算法常常用于解决以下类型的问题:1. 排序问题2. 查找问题3. 字符串匹配问题4. 最大子序列和问题5. 矩阵乘法问题6. 图形问题下面我们将一一讲解这些问题的分治算法实现。
1. 排序问题排序问题是在一组数据中将其按指定规律进行排列的问题。
在计算机科学中,排序算法是十分重要的一类算法。
其中,分治算法由于其高效性和可并行性被广泛应用。
常用的分治排序算法包括归并排序和快速排序。
归并排序的基本思想是将待排序元素以中心点为界分成两个序列,对每个序列进行排序,然后将两个序列合并成一个有序序列;而快速排序则利用了分割的思想,通过每次选取一个元素作为“轴点”,将数组分成小于轴点和大于轴点的两部分,对这两部分分别进行快速排序。
2. 查找问题查找问题是在一组数据中寻找某个元素的问题。
分治算法在查找问题中的应用主要体现在二分查找中。
在二分查找中,我们首先将已排序的数组分成两半,在其中一半中查找目标值。
1、给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?方案1:可以估计每个文件安的大小为50G×64=320G,远远大于内存限制的4G。
所以不可能将其完全加载到内存中处理。
考虑采取分而治之的方法。
s 遍历文件a,对每个url求取,然后根据所取得的值将url分别存储到1000个小文件(记为)中。
这样每个小文件的大约为300M。
s 遍历文件b,采取和a相同的方式将url分别存储到1000个小文件(记为)。
这样处理后,所有可能相同的url都在对应的小文件()中,不对应的小文件不可能有相同的url。
然后我们只要求出1000对小文件中相同的url即可。
s 求每对小文件中相同的url时,可以把其中一个小文件的url存储到hash_set中。
然后遍历另一个小文件的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。
方案2:如果允许有一定的错误率,可以使用Bloom filter,4G内存大概可以表示340亿bit。
将其中一个文件中的url使用Bloom filter映射为这340亿bit,然后挨个读取另外一个文件的url,检查是否与Bloom filter,如果是,那么该url应该是共同的url(注意会有一定的错误率)。
2、有10个文件,每个文件1G,每个文件的每一行存放的都是用户的query,每个文件的query都可能重复。
要求你按照query的频度排序。
方案1:s、顺序读取10个文件,按照hash(query)的结果将query写入到另外10个文件(记为)中。
这样新生成的文件每个的大小大约也1G(假设hash函数是随机的)。
s、找一台内存在2G左右的机器,依次对用hash_map(query, query_count)来统计每个query出现的次数。
利用快速/堆/归并排序按照出现次数进行排序。
矩阵最高阶非零子式的求法矩阵最高阶非零子式的求法是线性代数中的一个重要问题。
在矩阵理论中,矩阵的子式是指从矩阵中任意选取若干行和若干列所组成的新矩阵的行列式。
矩阵的最高阶非零子式是指所有非零子式中行列数最大的那个子式。
求解矩阵的最高阶非零子式是矩阵理论中的一个基本问题,对于矩阵的性质分析和应用具有重要意义。
求解矩阵的最高阶非零子式的方法有很多种,其中比较常用的方法有以下几种:1. 高斯消元法高斯消元法是求解线性方程组的常用方法,也可以用来求解矩阵的最高阶非零子式。
具体方法是将矩阵进行初等变换,使得矩阵的某些行或列变为零,然后计算变换后的矩阵的行列式。
如果变换后的矩阵的某个子式不为零,则该子式就是原矩阵的一个非零子式。
重复进行初等变换,直到无法再进行初等变换为止,得到的最高阶非零子式就是原矩阵的最高阶非零子式。
2. LU分解法LU分解是将一个矩阵分解为一个下三角矩阵和一个上三角矩阵的乘积的方法。
具体方法是先对矩阵进行初等变换,将其变为一个上三角矩阵,然后再将其变为一个下三角矩阵。
在这个过程中,可以计算出每一步变换后的矩阵的行列式,从而得到原矩阵的所有非零子式。
最后,取行列数最大的非零子式作为原矩阵的最高阶非零子式。
3. 特征值法特征值法是求解矩阵特征值和特征向量的方法,也可以用来求解矩阵的最高阶非零子式。
具体方法是先求解矩阵的特征值和特征向量,然后将特征向量组成的矩阵进行初等变换,得到一个上三角矩阵。
最后,取上三角矩阵的对角线上的元素的乘积作为原矩阵的最高阶非零子式。
总之,求解矩阵的最高阶非零子式是线性代数中的一个基本问题,有多种求解方法。
在实际应用中,可以根据具体问题的特点选择合适的方法进行求解。
【java】矩阵的最⼤⼦矩阵(动态规划)⼀、实验⽬的练习使⽤动态规划算法解决实际问题(使⽤Java语⾔实现)。
⼆、实验内容【问题描述】有⼀个包含正数和负数的⼆维数组。
⼀个⼦矩阵是指在该⼆维数组⾥,任意相邻的下标是1*1或更⼤的⼦数组。
⼀个⼦矩阵的和是指该⼦矩阵中所有元素的和。
本题中,把具有最⼤和的⼦矩阵称为最⼤⼦矩阵。
【⽰例】给出以下⼆维数组:0 -2 -7 09 2 -6 2-4 1 -4 1-1 8 0 -2这个数组的最⼤⼦矩阵为:9 2-4 1-1 8其和为15。
【输⼊】输⼊包含多组测试数据。
每组输⼊的第⼀⾏是⼀个正整数N(1<=N<=100),表⽰⼆维⽅阵的⼤⼩。
接下来N⾏每⾏输⼊N个整数,表⽰数组元素,范围为[-127,127]。
【输出】输出最⼤⼦矩阵和。
【思路提⽰】求最⼤⼦矩阵和问题是求最⼤⼦段和问题在⼆维空间上的推⼴,可参考求最⼤⼦段和问题。
三、 程序代码(1)maxSumList1package maxSumList;2import java.util.Scanner;34public class maxList{5 //public static int[][] list=new int[10][10];6 static int n;7 private maxSingleList maxSingleList=new maxSingleList();8 private final maxSingleList[] maxSingleLists;9 private int numberOfmaxSingleLists;1011 public maxList() {12 //创建计算每个⼦段和的类的数组13 maxSingleLists=new maxSingleList[100];14 }15161617 public void InputList(int[][] list){1819 System.out.println("请输⼊⽅阵⼤⼩:");20 Scanner scanner=new Scanner(System.in);21 n=scanner.nextInt();22 for (int y=0;y<n;y++){23 System.out.println("请输⼊⽅阵第"+(y+1)+"⾏数据:");24 for (int x=0;x<n;x++)25 list[y][x]=scanner.nextInt();26 }27 }28 public void OutputMaxSumList(int[][] list){29 int m=0;30 int max=0;31 int max1=0;32 int maxnum=0;3334 for (m=0;m<=numberOfmaxSingleLists;m++) {35 max1=maxSingleLists[m].getSum();36 if (max1 > max) {37 max = max1;38 maxnum = m;39 }40 }41 System.out.println("请输出最⼤⼦段和:"+maxSingleLists[maxnum].getSum());42 System.out.println("请输出最⼤⼦段:");43 for(int i=maxSingleLists[maxnum].getY1();i<=maxSingleLists[maxnum].getY2();i++){44 for (int j=maxSingleLists[maxnum].getNum1();j<=maxSingleLists[maxnum].getNum2();j++){45 System.out.print(list[i][j]+" ");46 }47 System.out.println("\n");48 }49 }5051 public void subMaxList(int[][] matrix) {52 int m=0;53 int[][] total = new int[10][10];54 for (int y=0;y<n;y++){55 for (int x=0;x<n;x++)56 total[y][x]=matrix[y][x];57 }585960 for (int i = 1; i < n; i++) {61 for (int j = 0; j < n; j++) {62 total[i][j] += total[i-1][j];63 }64 }6566 int maximum = 0;//Integer.MIN_VALUE;67 for (int i = 0; i < n; i++) {//所在的list⾏68 for (int j = i; j <n; j++) {//相差的69 //result 保存的是从 i ⾏到第 j ⾏所对应的矩阵上下值的和70 int[] result = new int[matrix[0].length];//每次都重新定义存放⼦段和的结果数组71 for (int f = 0; f < n; f++) {72 if (i == 0) {73 result[f] = total[j][f];74 } else {75 result[f] = total[j][f] - total[i - 1][f];76 }77 }78 maxSingleList maxSingleList=new maxSingleList();79 int maximal=maxSingleList.MaxListNum(result,i,j);80 numberOfmaxSingleLists=m;81 maxSingleLists[m++]= maxSingleList;81 maxSingleLists[m++]= maxSingleList;82 if (maximal > maximum) {83 maximum = maximal;84 }85 }86 }87 }8889}(2)maxSingleList4 private int num1;5 private int num2;6 private int y1;7 private int y2;8 private int sum;9 public int getNum1(){10 return num1;11 }12 public int getNum2(){13 return num2;14 }15 public void setY1(int y11){16 y1=y11;17 }18 public void setY2(int y22){19 y2=y22;20 }21 public int getY1(){22 return y1;23 }24 public int getY2(){25 return y2;26 }27 public void setSum(int sum){28 this.sum=sum;29 }30 public int getSum(){31 return sum;32 }33 public int MaxListNum(int[] array,int i,int j){34 int number,b=0,begin=0,bestmin=0,bestmax=0;35 sum=0;36 for (number = 0; number < array.length; number++) {//sum没清零37 if (b >= 0)//去掉等号38 b += array[number];39 else {40 b = array[number];41 begin = number;42 }43 if (b > sum) {//加个+44 sum = b;45 bestmin = begin;46 bestmax = number;47 }4849 }50 num1 = bestmin;51 num2= bestmax;52 setSum(sum);53 setY1(i);54 setY2(j);55 return sum;56 // if (sum==0)和为0的⾏数组要去掉那⼀⾏5758 }5960 }(3)TestmMaxList4 public static int[][] list=new int[10][10];5 public static void main(String[] arg){6 maxList maxlist=new maxList();7 maxlist.InputList(list);8 maxlist.subMaxList(list);9 maxlist.OutputMaxSumList(list); 1011 }12}四、 实验结果(含程序运⾏截图)五、 出现问题及解决⽅法(⼀)出现的问题在于算法的设计上,⼀开始我认为最⼤⼦矩阵就是每⾏所构成的最⼤⼦段的⾏列的序号交集,后来发现不是这样的,这样没办法正确输出最⼤⼦矩阵,得到的结果不对,然后推翻⾃⼰写了⼀天的代码以及想法。
浅谈最⼤⼦矩阵浅谈最⼤⼦矩阵问题【最⼤⼦矩阵问题】最⼤⼦矩阵问题是⼀类求解某个矩阵中最⼤符合条件的⼦矩阵的问题,⼀般的条件有⼦矩阵不能覆盖障碍点、⼦矩阵的形状等。
【极⼤化思想】这⾥⾸先解释⼏个概念:有效⼦矩阵:满⾜题⽬要求的⼦矩阵。
极⼤⼦矩阵:满⾜题⽬要求的、边界⽆法再扩张的⼦矩阵。
最⼤⼦矩阵:极⼤⼦矩阵中最⼤的⼀个。
在许多问题中,我们常常见到形如“使xx⾯积最⼤”“找到最⼤的矩形”的提问,实际上就是要我们求解最⼤⼦矩阵。
所谓极⼤化思想,其实就是枚举极⼤⼦矩阵,找到其中最⼤的⼀个。
【如何求解极⼤⼦矩阵】上⾯提到要枚举极⼤⼦矩阵,那么显然,前提是我们要求解所有极⼤⼦矩阵。
⼀般⽽⾔,我们有两种算法来求解所有极⼤⼦矩阵。
我们⾸先定义⼀些符号:n表⽰矩阵的⾼,m表⽰矩形的宽,k表⽰矩形中障碍点的数量。
边界扩展法(我瞎起的名字)根据极⼤⼦矩阵的定义,其边界⽆法再扩张,那么显然其边界要么在障碍物所在⾏/列(即恰好覆盖障碍点),要么与整个矩阵的边界重合。
因此,我们不妨从障碍点⼊⼿,不断扩张极⼤⼦矩阵,扩展它的边界。
显然有⼀种做法就是枚举所有可能构成矩形的边界,找出最⼤⼦矩阵,但是复杂度感⼈。
那么如何优化呢?我们要使得这个优化尽量少的枚举不是极⼤⼦矩阵的矩阵,还要确保这些⼦矩阵是合法的。
我们不妨线性的逐⾏扩展最⼤⼦矩阵的某⼀个边界,然后根据其它障碍点的位置维护其它边界的位置。
具体⽽⾔,就是先对所有障碍点按列升序排序,然后依次枚举这些障碍点,以此次枚举的障碍点所在的列作为极⼤⼦矩阵左边界,然后将右边界向右扩展,在扩展右边界时,我们还要维护上下边界,使得这些⼦矩阵满⾜合法性。
具体如何维护上下边界,这边窝弄了⼏张⼤佬的图(不要打我):这个已经讲的很清楚了,我就不必多⾔了。
例题 P1578 奶⽜浴场题⽬描述由于John建造了⽜场围栏,激起了奶⽜的愤怒,奶⽜的产奶量急剧减少。
为了讨好奶⽜,John决定在⽜场中建造⼀个⼤型浴场。
力扣题目分类力扣是一家全球知名的在线编程学习和竞赛平台,提供了众多的算法题目和数据结构练习题,适合各个阶段的程序员练习和提高。
在这些题目中,除了一些基础的算法题目之外,还有很多题目涉及到各种算法模型和数据结构,如动态规划、贪心、搜索、二分查找、哈希表、堆、树等等。
在这里,我们将这些题目进行分类,以便大家更好地学习和练习。
1.动态规划(Dynamic Programming)分类动态规划是一种常用的算法思想,通常用于优化问题中,可以将问题拆分成多个子问题,使得原问题的解可以由子问题的解推导出来。
下面是力扣上一些动态规划相关的题目分类:- 斐波那契数列问题- 背包问题- 矩阵路径问题- 最长公共子序列问题- 最长上升子序列问题- 最大子序和问题- 最大子数组乘积问题2.贪心(Greedy)分类贪心算法是一种常用的算法思想,通常用于求解优化问题中,在每一步选择中都采取当前状态下最优的选择,从而希望得到全局最优解。
下面是力扣上一些贪心相关的题目分类:- 区间问题- 贪心分配问题- 最小生成树问题3.搜索(Search)分类搜索算法是一种常用的算法思想,通常用于在大规模的数据集中寻找特定的目标,如寻找路径、寻找最短路经、寻找连通块、寻找最大值等。
下面是力扣上一些搜索相关的题目分类:- 深度优先搜索(DFS)- 广度优先搜索(BFS)- 回溯搜索- A*搜索算法4.二分查找(Binary Search)分类二分查找是一种常用的算法思想,通常用于在有序的数据集中查找指定的元素,可以在O(log n)的时间内完成查找。
下面是力扣上一些二分查找相关的题目分类:- 寻找旋转排序数组中的最小值- 在排序数组中查找元素的第一个和最后一个位置- 寻找峰值元素- 有效的完全平方数5.哈希表(Hash Table)分类哈希表是一种常用的数据结构,通常用于在O(1)的时间内完成元素的查找、插入、删除等操作,其核心是哈希函数的设计。
动态规划算法有啥用途动态规划算法是一种常用的优化算法,可以在时间和空间上实现高效的计算。
它适用于一系列问题,包括最优化问题、决策问题和计数问题等。
动态规划算法通常用于问题具备「无后效性」(无后效性是指问题的当前状态不会受到未来状态的影响)和「最优子结构」(问题的最优解可以由子问题的最优解推导得到)的情况下。
基本思想是将原问题划分为若干子问题,逐个求解子问题,再根据子问题的最优解推导出原问题的解。
下面将介绍几个典型的应用场景:1. 最短路径问题:最短路径问题是图论中的经典问题,动态规划算法可以高效地解决。
通过构建状态转移方程,可以递推求解从起点到终点的最短路径。
2. 最长公共子序列问题:最长公共子序列问题在字符串处理中非常常见,例如求两个字符串的最长公共子序列长度。
动态规划算法可以通过构建状态转移方程来高效地求解。
3. 背包问题:背包问题是一类经典的组合优化问题,常见的有0-1背包问题、完全背包问题和多重背包问题。
动态规划算法可以用来求解背包问题的最优解。
4. 最大子数组和问题:最大子数组和问题是在一个数列中找到一个连续子数组,使得子数组元素的和最大。
动态规划算法可以用来高效地求解最大子数组和。
5. 最长递增子序列问题:最长递增子序列问题即求解一个序列中最长的子序列,满足子序列中的元素从左到右递增。
动态规划算法可以高效地求解最长递增子序列的长度。
6. 矩阵链乘法问题:矩阵链乘法问题是矩阵计算中常见的优化问题,即给定一系列矩阵,求解它们相乘的最少次数。
动态规划算法可以用来高效地解决该问题。
7. 0-1背包问题:0-1背包问题是指在给定的一组物品中,每个物品可以选择放入背包或不放入背包,目标是使得背包中物品的总价值最大,且背包的容量不能超过一个给定的值。
动态规划算法可以用来求解该问题的最优解。
8. 最大子矩阵和问题:最大子矩阵和问题是在一个二维矩阵中寻找一个子矩阵,使得子矩阵元素的和最大。
动态规划算法可以用来高效地求解最大子矩阵和。
分治法有哪些经典用途分治法是一种常见的算法思想,它的核心思想就是将一个问题分解成多个子问题,然后解决各个子问题,最后将各个子问题的结果合并,从而得到原问题的解决方案。
分治法一般可以分为三个步骤:分解问题、解决子问题、合并子问题结果。
分治法可以用来解决许多经典问题,下面将介绍一些常见的应用。
1. 排序排序可以说是计算机程序中最常见的问题之一,而分治法则是其中的一种经典算法思想。
经典的归并排序算法就是一种基于分治法的排序算法。
该算法将数组分解成两个子数组,分别进行递归排序,最后将两个子数组合并成一个有序数组。
2. 最大子序列和问题最大子序列和问题是一个在数组中寻找一个连续子序列,使得该子序列中的元素和最大的问题。
该问题可以使用分治法来解决。
将数组分成两半,分别计算左半边、右半边以及横跨两个子数组的最大子序列和。
最后将这些结果合并,找出最大的子序列和。
3. 二分搜索二分搜索是一种常见的查找算法,它可以在有序数组中快速查找指定元素。
该算法也是一个基于分治法的算法。
将数组分成两半后查看中间元素,如果中间元素等于指定元素,则查找结束。
如果中间元素大于指定元素,则在左边的子数组中查找。
如果中间元素小于指定元素,则在右边的子数组中查找。
4. 逆序对问题逆序对问题是一个在数组中寻找所有逆序对个数的问题。
逆序对指的是在一个数组中,如果i<j且a[i]>a[j],则称(a[i], a[j])是一个逆序对。
这个问题可以利用分治法来解决,将数组分成两个子数组,分别计算左半边、右半边以及跨越两个子数组的逆序对数。
最后将这些结果合并,得到所有逆序对的个数。
5. 矩阵乘法矩阵乘法是一个重要的数学问题,也是在计算机领域中广泛应用的问题之一。
分治法可以用来加快矩阵乘法的计算。
将两个矩阵分成四个子矩阵后,可以利用递归方式对每个子矩阵进行矩阵乘法计算,最后将结果合并得到最终的乘积矩阵。
6. 凸包问题凸包问题是计算机几何学中的一个经典问题,它的主要目标是求出一个点集的凸包,即包含给定点集的最小凸多边形。
学习材料:王知昆《浅谈用极大化思想解决最大子矩阵问题》
【最大子矩阵问题】
在一个给定的矩形中有一些障碍点,找出内部不包含障碍点的、轮廓与整个矩形平行或重合的最大子矩形。
【定义子矩形】
有效子矩形:内部不包含障碍点的、轮廓与整个矩形平行或重合的子矩形。
极大子矩形:每条边都不能向外扩展的有效子矩形。
最大子矩形:所有有效子矩形中最大的一个(或多个)。
【极大化思想】
在一个有障碍点的矩形中最大子矩形一定是极大子矩形。
设计算法的思路:枚举所有的极大子矩形,找到最大子矩形。
设NM分别为整个矩形的长和宽,S为内部的障碍点数。
【算法1】
时间复杂度:O(S^2)空间复杂度:O(S)
由于极大子矩形的每一条边都不能向外扩展,那么极大子矩阵的每条边要么覆盖了障碍点,要么与整个矩形的边界重合
基本算法:枚举上下左右四个边界,然后判断组成的矩形是否是有效子矩形。
复杂度:O(S^5)可以改进的地方:产生了大量的无效子矩形。
初步改进的算法:枚举左右边界,然后对处在边界内的点排序,每两个相邻的点和左右边界组成一个矩形。
复杂度:O(S^3)可以改进的地方:枚举了部分不是极大子矩形的情况。
综上,设计算法的方向:
1、保证每一个枚举的矩形都是有效的。
2、保证每一个枚举的矩形都是极大的。
算法的过程:
枚举极大子矩形的左边界——>根据确定的左边界,找出相关的极大子矩形——>检查和处理遗漏的情况
(1)按照横坐标从小到大的顺序将所有的点编号为1,2,3...
(2)首先选取1号点作为要枚举的极大子矩形的左边界,设定上下边界为矩形的上下边界
(3)从左到右扫描,第一次到2号点,确定一个极大子矩形,修改上下边界;第二次找到3号点,以此类推。
(4)将左边界移动到2号点,3号点,,,以同样的方法枚举
遗漏的情况:
1、矩形的左边界与整个矩形的左边界重合。
解决方法:用类似的方法从左到右扫一遍
2、矩形的左边界与整个矩形的左边界重合,且矩形的右边界与整个矩形的右边界重合。
解决方法:预处理时增加特殊判断。
优点:利用的极大化思想,复杂度可以接受,编程实现简单。
缺点:使用有一定的局限性,不适合障碍点较密集的情况。
【算法2】
时间复杂度O(NM) 空间复杂度O(NM)
定义
有效竖线:除了两个端点外,不覆盖任何一个障碍点的竖直线段。
悬线:上端覆盖了一个障碍点或者到达整个矩形上边界的有效线段。
每个悬线都与它底部的点一一对应,矩形中的每一个点(矩形顶部的点除外)都对应了一个悬线。
悬线的个数=(N-1)*M;
如果把一个极大子矩形按照横坐标的不同切割成多个与y轴平行的线段,那么其中至少有一个悬线。
如果把一个悬线向左右两个方向尽可能的移动,那么就得到了一个矩形,我们称它为悬线对应的矩形。
悬线对应的矩形不一定是极大子矩形,因为下边界可能还可以向下扩展。
设计算法:
原理:所有悬线对应矩形的集合一定包含了极大子矩形的集合。
通过枚举所有的悬线,找出所有的极大子矩形。
算法规模:
悬线个数=(N-1)×M
极大子矩形个数≤悬线个数
具体方法:
设H[i,j]为点(i,j)对应的悬线的长度。
L[i,j]为点(i,j)对应的悬线向左最多能够移动到的位置。
R[i,j]为点(i,j)对应的悬线向右最多能够移动到的位置。
!考虑点(i,j)对应的悬线与点(i-1,j)对应的悬线的关系(递推思想):
如果(i-1,j)为障碍点,那么,如图所示,(i,j)对应的悬线长度1,左右能移动到的位置是整个矩形的左右边界。
即H[i,j]=1,
L[i,j]=0,R[i,j]=m
如果(i-1,j)不是障碍点,那么,如图所示,(i,j)对应的悬线长度为(i-1,j)对应的悬线长度+1。
即H[i,j]=H[i-1,j]+1
•如果(i-1,j)不是障碍点,那么,如图所示,(i,j)对应的悬线左右能移动的位置要在(i-1,j)的基础上变化。
L[i-1,j]
L[i,j]=max
(i-1,j)左边第一个障碍点的位置
•同理,也可以得到R[i,j]的递推式
R[i-1,j]
R[i,j]=min
(i-1,j)右边第一个障碍点的位置
优点:复杂度与障碍点个数没有直接关系。
缺点:障碍点少时处理较复杂,不如算法1。
代码实现具体见WC2002 奶牛浴场(算法1)
codevs1159最大全0子矩阵(算法2)。