题目:二维数组中求其最大的矩形子数组之和

相关要求:

1,以指定格式的文本文件形式输入数组。

2,数组由一维变为二维。

3,给出单元测试/代码覆盖率的最终覆盖率的报告

即是要求输入:一个二维数组和它的大小 ;输出:这个二维数组中最大的矩形子数组的和

例如:

输入的二维数组 输出结果

1 2 3 4
-1 7 8 -4
1 2 2 2

 27

1 -2 -3 4
-1 7 8 -4
1 2 2 2

 19

-1 -2 -3 -4
-1 -7 -8 -4
-1 -2 -2 -2

 -1

思路:1.因为要求制定的文本格式,所以我们要制定文本输入的格式,比如下面这样,第一行输入行列数,第二行开始输入二维数组

 又比如第一行输入行数,第二行输入列数,第三行开始输入二维数组如下这样:

 2.指定了规定的文本输入,接下里就是二维数组求解问题,首先得到子数组的最大行、列数,从第一行开始,确定最大子数组的行列范围。然后将确定的范围将最大子数组按照一列有几个数按行分开(例如第一个组中只有一行,而第二个组中有两行,列数和数组的列数保持一致)。这样对上面得到的数组按照一维数组求和的方法找出最大值比较得到最大的那一个,最后是行列的最大上界减一重复前面的过程不断比较就能得到最后的最大子数组的和。

 

一、实现代码

  • C++:vs2010实现,结果和代码如下:
 #include<iostream>
 #include <stdio.h> 
  using namespace std;
  
 FILE *fpRead=fopen("input.txt","r");
  
  char cgetchar(){
     char c;
     fscanf(fpRead,"%c",&c);
     return c;
 }
 
 int getnum(){
     char c;
     int num = 0, fuhao = 1;
     while((c = cgetchar()) != EOF && ('0' > c || '9' < c) && c != '-');
     if(c == '-') fuhao = -1;
     else num = c - '0';
     while((c = cgetchar()) != EOF && '0' <= c && '9' >= c){
         num = num * 10 + c - '0';
     }
     return num * fuhao;
 }


 int max()
 {
     int x,y,i,j,m=0,A[100][100];
  
     x = getnum();
     y = getnum();
     if(x>100||y>100)
     {
         cout<<"请重新输入:";
         cin>>x>>y;
     }
     cout<<"二维数组为:"<<endl;
     for(i=0;i<x;i++)
    {
        for(j=0;j<y;j++)
        {
            A[i][j] = getnum();
            printf("%d ", A[i][j]); 
         }
         printf("\n");
  
     }
     int sum[100]={0},max=0,result=A[0][0];
  
     for(i=0;i<x;i++)//确定子数组的最大上界(为第i行)
     {
         while(m+i<x)//确定子数组有m+i行
         {
             //把子数组当成一位数组一样,求最大子数组的和             
             for(j=0;j<y;j++)
            {
                sum[j]=sum[j]+A[m+i][j];
  
             }
            max=0;
             for(j=0;j<y;j++)
             {
                 if(max+sum[j]>sum[j])
                 {
                     max=max+sum[j];
                 }
                 else
                 {
                     max=sum[j];
                 }
                 if(max>result)
                 {
                     result=max;
                 }
             }
             m++;//是子数组的行数+1
         }
         m=0;
         for(j=0;j<y;j++)
         {
             sum[j]=0;
         }
     }
    /* cout<<"最大子数组和为:"<<result<<endl;*/
     return result;

 }
 
 int main ()
 {
     
     cout<<"最大子数组和为:"<<max()<<endl;
     return 0;
 }

结果:

 

 

   开始是准备用vs2010直接进行单元测试和代码覆盖率的,查阅了资料需要创建.dll项目才能进行测试。后面一直卡在成功生成dll项目那里所以最后还是改用了java来进行单元和代码覆盖率的测试。

如果看文章的您能用vs2010直接进行这个二维数组的单元测试的话,也希望您能教教孩子,感谢感谢哈哈哈哈。【救救孩子.jpg】【哈哈哈哈.jpg】

 

  • 用java代码实现的二维数组求其最大子数组的和,代码如下:
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;

public class Max {
public static void main(String[] args) throws IOException {

    Integer[][] c=new Integer[100][];
    
    c=ReadFile("C:\\Users\\DELL\\Desktop\\input.txt");
    for(int i=0;i<c.length;i++){
        for(int j=0;j<c[0].length;j++){    
            System.out.print(c[i][j]+" ");
        }
        
        System.out.println();
    }
    System.out.println("1:"+GetMaxSUM(c));
}

public static Integer[][] ReadFile(String str){
    FileReader file = null;
    
    try {
        file = new FileReader(str);
    } 
    catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    System.out.println("文件测试数据如下:");
    BufferedReader br = new BufferedReader(file);//读取文件
    try {
        String line = br.readLine();//读取一行数据
        int lenx = Integer.parseInt(line);//将数据转化为int类型
        line = br.readLine();//读取一行数据
        int leny = Integer.parseInt(line);//将数据转化为int类型
        // System.out.println(lines);
        
        String []sp = null;
        String [][]c = new String[lenx][leny];
        Integer[][] cc = new Integer[lenx][leny];
        int count=0;
        while((line=br.readLine())!=null) {//按行读取
            sp = line.split(" ");//按空格进行分割
            for(int i=0;i<sp.length;i++){
            c[count][i] = sp[i];
        }
        count++;
        }
        for(int i=0;i<lenx;i++){
            for(int j=0;j<leny;j++){
            cc[i][j] = Integer.parseInt(c[i][j]);
            // System.out.print(cc[i][j]+" ");
            }
            // System.out.println();
        }
        return cc;
    } 
    catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

public static int GetMaxSUM(Integer[][] a){
    int n=a.length;
    int m=a[0].length;
    //分块
    Integer[] Max=new Integer[100];
    Integer[] Begin=new Integer[100];
    Integer[] End=new Integer[100];
    Integer[] b=new Integer[100];
    for(int i=0;i<n;i++)
    {
        //按行分组
        for(int j=0;j<m;j++)
        {
            b[j]=a[i][j];
        }
        MaxIntArray(b,Max,Begin,End,m,i);
    }
    
    int max=Max[0];
    for(int i=0;i<n-1;i++)
    {
    if((Begin[i]<=End[i+1]&&Begin[i]>=Begin[i+1])||(End[i]<=End[i+1]&&End[i]>=Begin[i+1]))
    {
        max=Max[i+1]+max;
    }
    else
    {
        //如果不能直接连通,判断代价是否合适
        if(Begin[i]>End[i+1])
        {
            int t = Begin[i]-End[i+1];
            int s = Begin[i];
            int temp=0;
            for(int k=0;k<t;k++)
            {
                temp+=a[i+1][s-k];
            }
            if(temp+Max[i+1]>0)
                max=temp+Max[i+1];
        }
        if(End[i]<Begin[i+1])
        {
            int t = Begin[i+1]-End[i];
            int s = End[i];
            int temp=0;
            for(int k=0;k<t;k++)
            {
                temp+=a[i+1][s+k];
            }
            if(temp+Max[i+1]>0)
                max=temp+Max[i+1];
            }
    }
    }
    return max;
}
public static void MaxIntArray(Integer[] a,Integer[] max,Integer[] begin,Integer[] end,int n,int index){

    Integer[] Max=new Integer[100];
    Max[0] = 0;
    int i = 0;//数组下标
    int j = 0;//最大值数组下标
    int temp=0;//中间变量
    //记录子数组的起始位置和末位
    // Integer[] Bg=new Integer[100];
    Integer[] Bg={-1,-1,-1,-1,-1};
    Integer[] Ed=new Integer[100];
    while(i<n){
    if(temp+a[i]>=Max[j])
    {
        temp=temp+a[i];
        Max[j]=temp;
        if(Bg[j]==-1)
        Bg[j]=i;
        Ed[j]=i;
        i++;
    }
    else if(temp+a[i]<Max[j]&&temp+a[i]>0)
    {
        temp=temp+a[i];
        i++;
    }
    else if(temp+a[i]<=0)
    {
        i++;
        j++;
        Max[j]=0;
        temp=0;
    }
    
    }
    max[index] = Max[0];
    int q=0;
    for(int k=0;k<=j;k++){
        if(Max[k]>max[index])
        {
            max[index]=Max[k];
            q=k;
        }
    }
    begin[index]=Bg[q];
    end[index]=Ed[q];
    }
}

 

二、单元测试

  1. 选择直接在eclipse里面进行单元测试,选择Junit Test Case创建测试类
  2. 创建测试用例:一共创建了三组测试用例如下表格所示
input.txt

3
4
1 2 3 10
3 4 5 2
2 4 5 4

input1.txt

3
4
-4 9 3 6
3 -6 1 6
3 9 4 -5

input2.txt  

2
3
-1 -2 1
-1 -1 2

 

  注意:这里输入的时候第一行和第二行不能输入空格在回车,否则会直接报错。

  3.测试代码:在Text类里编写测试的代码如下所示:


import static org.junit.Assert.*;
import static test.Max.Max;
import static test.Max.MaxSUM;
import static test.Max.ReadFile;

public class Max{

    @Test
    public void test1() {
        String str="C:\\Users\\DELL\\Desktop\\input.txt";
        Integer[][] c=ReadFile(str);
        System.out.println("Max:"+Max(c));
    }
    @Test
    public void test2() {
        String str="C:\\Users\\DELL\\Desktop\\input2.txt";
        Integer[][] c=ReadFile(str);
        System.out.println("Max:"+Max(c));
    }
    @Test
    public void test3() {
        String str="C:\\Users\\DELL\\Desktop\\input3.txt";
        Integer[][] c=ReadFile(str);
        System.out.println("Max:"+Max(c));
    }
}

  4.测试结果:进行的三个都测试成功

 

 

 

 

 

三、代码覆盖率

  • 要进行代码覆盖率的测试就需要先安装Jacoco插件

  按照查找到的资料按步骤安装:Eclipse下依次点击 Help->Eclipse Marketplace->搜索EclEmma,接下里就按照步骤安装,安装好后在Window->Show View->Other中找到Coveage可以打开界面可以打开

  • 覆盖率检测

  选择想要测试的文件或者文件夹,右键项目选择Coverage As -> JUnit Test

得到统计图如下所示:

  对程序代码进行代码覆盖率检测(部分):

 

 有关覆盖率的三种颜色:

绿色:代码被执行过

黄色:部分代码被执行过

红色:代码没有被执行过

    通过代码覆盖率这样我们就可以直观的看到哪些代码是被执行了,哪些是部分执行,哪些是没有执行的。经过多次的执行程序,每一次选用不同的测试案例,这样就可以多次看到代码的执行情况,粗略看到整个项目的代码执行情况

小结

  • 在java进行单元测试时并没有测试全面,对于其中一些异常情况,比如空指针等没有进行代码测试因为其会直接报错,同时对于全为负数的二维数组也没有测试,如果其不成功的话后续也要对代码进行进一步改进
  • 因为测试用例不够,所以整个项目的代码覆盖率也是不完整的,如果要详细得到整个项目所有的代码覆盖率至少要把所有的代码测试用例都执行一遍,这样相对比较麻烦。
  • 通过代码测试覆盖率我们也能得到一些有用的信息,比如代码测试覆盖率比较高则能看出代码安全性会比较能保证。
  • 二维数组求其最大子数组和还有很多其他的情况,此次我们考虑的只是里面小矩形的最大值,还可以是环形的,最小连通那样的各种形状得到的结果。所以后续有兴趣也会对这些做一些了解