csp 邻域均值题解

在这里插入图片描述
在这里插入图片描述

样例输入

4 16 1 6
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15

样例输出

7

样例输入

11 8 2 2
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 7 0 0 0 7 0 0 7 7 0
7 0 7 0 7 0 7 0 7 0 7
7 0 0 0 7 0 0 0 7 0 7
7 0 0 0 0 7 0 0 7 7 0
7 0 0 0 0 0 7 0 7 0 0
7 0 7 0 7 0 7 0 7 0 0
0 7 0 0 0 7 0 0 7 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0

样例输出

83

评测用例规模与约定

在这里插入图片描述
     在看到这个题后,想着暴力能做出来,但是,觉得太麻烦,就不想用暴力做,发现暴力还过不了,果断放弃

     这里先讲一下题目的大概意思,就是题目会先输入四个数据,n表示n*n的矩阵,后面就会输入矩阵,l表示矩阵中的元素最大不会超过l,r表示某个元素半径为r的范围内,t表示在半径为r的范围内的平均值要是小于t就表明这个元素是符合要求的,最后就是要输出符合要求的点的个数。

     很明显我们需要求某个元素半径为r内的元素之和和元素个数,这个时候就要用到前缀和,通过前缀和来确定目标区域的和。

     这个题目可以用一维的前缀和,也可以用二维的前缀和,来说说他们有什么区别。一维的前缀和就是一行一行求,它的sum数组里的每个值只代表这一行里,这个元素和他前面的元素的和,二维数组就是不仅求了行的和,也要求列的和。
  下面是一维数组前缀和的代码,一维前缀和就需要额外判断下第一个元素

public class Main {
    public static void main(String[] args) {
       int[] sum=new int[5];
       int[] arr={1,2,3,4};
       for(int i=0;i<4;i++) {
           if(i==0)
               sum[i]=arr[i];
           else sum[i]=sum[i-1]+arr[i];
       }
       for(int i=0;i<4;i++) {
           System.out.println(sum[i]);
       }
    }
}

也有另一种方法算一维前缀和,也是空间换取时间的做法

    public NumMatrix(int[][] matrix) {
        int m = matrix.length;
        if (m > 0) {
            int n = matrix[0].length;
            sums = new int[m][n + 1];
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    sums[i][j + 1] = sums[i][j] + matrix[i][j];
                }
            }
        }
    }

     可能有的人不太理解二维前缀和,我来举个栗子,就拿样例1来说吧,假设这个数组是arr
1  2   3    4
5   6   7   8
9  10  11 12
13 14 15 16

就从下标为1开始算吧
sum[1][1]=1
sum[1][2]=1+2=sum[1][1]+arr[1][2]=3…
sum[2][1]=1+5=6
sum[2][2]=1+2+5+6=sum[1][2]+sum[2][1]-sum[1][1]+arr[2][2]=14
sum[2][3]=1+2+3+5+6+7=sum[1][3]+sum[2][2]-sum[1][2]+arr[2][3]=6+14-3=17

通过上边的例子
我们可以看出来,在第一行的时候其实就是sum[i][j-1]+arr[i][j]
第一列就是sum[i-1][j]+arr[i[j]
在其他情况下就是sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+arr[i][j],可以这样求出前缀和
接下来看代码
因为第一行,第一列处理的方法不一样,所以就直接单拉出来

public class Main {
    public static void main(String[] args) {
        int[][] arr={{1,1,1,1},
                    {1,1,1,1},
                    {1,1,1,1},
                    {1,1,1,1}};
        int[][] sum=new int[4][4];
        sum[0][0]=arr[0][0];
        for(int i=1;i<4;i++) {
            sum[i][0]=sum[i-1][0]+arr[i][0];//第一列
            sum[0][i]=sum[0][i-1]+arr[0][i];//第一行
        }
        for(int i=1;i<4;i++) {
            for(int j=1;j<4;j++) {
                sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+arr[i][j];
            }
        }
        for(int i=0;i<4;i++) {
            for(int j=0;j<4;j++) {
                System.out.print(sum[i][j]+" ");
            }
            System.out.println("");
        }
    }
}

还有另一种方法算二维前缀和,是用空间换时间的做法,但是能一次性直接算出整个数组的前缀和

    public NumMatrix(int[][] matrix) {
        int m = matrix.length;
        if (m > 0) {
            int n = matrix[0].length;
            sums = new int[m + 1][n + 1];
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    sums[i + 1][j + 1] = sums[i][j + 1] + sums[i + 1][j] - sums[i][j] + matrix[i][j];
                }
            }
        }
    }

接下来就是看这道题,我这篇题解是用的一维前缀和来求的,我觉得这样做起来好处就是不会乱,比较好理解
还是按照例一来讲解
1  2   3    4
5   6   7   8
9  10  11 12
13 14 15 16

用一维数组求得的前缀和是
0   1   3    6
4   9  15  22
8  17  27  38
12 25 39  54
他们每行之间只是列有关系,行之间没有求和关系

知道了原理,现在就来看看代码

public class Main {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int L=sc.nextInt();
        int r=sc.nextInt();
        int t=sc.nextInt();
        int elem=0;
        int res=0;
        int[][] sum=new int[n+1][n+1];
        int[][] arr=new int[n+1][n+1];
        //求得前缀和
        for(int i=1;i<=n;i++) {
            for(int j=1;j<=n;j++) {
               arr[i][j]=sc.nextInt();
               //先求出来前缀和。,这里的前缀和是一维前缀和
               if(j==1)
                   sum[i][j]=arr[i][j];
               else
                   sum[i][j]=arr[i][j]+sum[i][j-1];
            }
        }

        int bx,by,ex,ey,cnt=0;
        for(int i=1;i<=n;i++) {
            for(int j=1;j<=n;j++) {
                //看有没有出界,表示正方形的上下左右
                bx=Math.max(i-r,1);//上边
                by=Math.max(j-r,1);//左边
                ex=Math.min(i+r,n);//下边
                ey=Math.min(j+r,n);//右边

                int ans=0;//用来判断正方形里元素的和
                //从上边到下边开始遍历
                for(int k=bx;k<=ex;k++) {
                    if(by==1)
                        ans+=sum[k][ey];
                    else ans+=sum[k][ey]-sum[k][by-1];
                }
                int num=(ex-bx+1)*(ey-by+1);//计算矩形里有多少个元素
                if(ans*1.0/num<=t)
                    cnt++;
            }
        }
        System.out.println(cnt);
    }
}

这个题要从头开始遍历,所以有的元素会越界,所以就需要判断下越界的情况,如果越界,这个矩形相应的边就变成了0,所以需要判断边界情况,bx就是beginx,ex就是endx

			    bx=Math.max(i-r,1);//上边
                by=Math.max(j-r,1);//左边
                ex=Math.min(i+r,n);//下边
                ey=Math.min(j+r,n);//右边

这四句话就把这个矩形的范围给定下来了,i-r确定的是上边界,如果越界了,上边界就是0,否则就是i-r,后面的同理

 			for(int k=bx;k<=ex;k++) {
                    if(by==1)
                        ans+=sum[k][ey];
                    else ans+=sum[k][ey]-sum[k][by-1];
                }
                int num=(ex-bx+1)*(ey-by+1);//计算正方形里有多少个元素
                if(ans*1.0/num<=t)
                    cnt++;
            }

在这里插入图片描述

如图所示,如果by==1,说明左边全部都得要,此时它的和就是
sum[1][2]+sum[2][2],因为sum[1][2]表示的是第一行中前两个数字的和,sum[2][2]表示的是第二行中前两个数字的和,他们加起来就是前两行中前两个数字的和,就是左上角四个数字的和。
当by!=1的时候
在这里插入图片描述
这样就能完成求和运算。
接下来就是平均值,要求平均值就得知道元素个数,我们已经知道了左边界,右边界,上边界,下边界,根据他们就能确定矩形的大小

 int num=(ex-bx+1)*(ey-by+1);//计算矩形里有多少个元素

最后求平均,比较,就能得出答案。

总结

     本题的考点在于前缀和,但是需要选择是二维前缀和还是一维前缀和,经过两个比较来看,我觉得一维的前缀和将元素之间的和分开,没有那么多的重复计算,这样更能简单,清楚地算出需要地和,所以我觉得这里用一维前缀和会更加方便一些。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值