代码随想录算法训练营打卡day2 |209.长度最小的子数组、59.螺旋矩阵、58. 区间和、44. 开发商购买土地

在这里插入图片描述

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] nums = new int[n][n];
        int startX = 0, startY = 0;  // 每一圈的起始点
        int offset = 1;
        int count = 1;  // 矩阵中需要填写的数字
        int loop = 1; // 记录当前的圈数
        int i, j; // j 代表列, i 代表行;

        while (loop <= n / 2) {

            // 顶部
            // 左闭右开,所以判断循环结束时, j 不能等于 n - offset
            for (j = startY; j < n - offset; j++) {
                nums[startX][j] = count++;
            }

            // 右列
            // 左闭右开,所以判断循环结束时, i 不能等于 n - offset
            for (i = startX; i < n - offset; i++) {
                nums[i][j] = count++;
            }

            // 底部
            // 左闭右开,所以判断循环结束时, j != startY
            for (; j > startY; j--) {
                nums[i][j] = count++;
            }

            // 左列
            // 左闭右开,所以判断循环结束时, i != startX
            for (; i > startX; i--) {
                nums[i][j] = count++;
            }
            startX++;
            startY++;
            offset++;
            loop++;
        }
        if (n % 2 == 1) { // n 为奇数时,单独处理矩阵中心的值
            nums[startX][startY] = count;
        }
        return nums;
    }
}


生成一个大小为 n x n 的螺旋矩阵,矩阵中的数字按顺时针螺旋顺序填充,从 1 开始,直到 n^2。

  1. 初始化二维数组
int[][] nums = new int[n][n];

创建一个大小为 n x n 的二维数组 nums,用于存放最终的螺旋矩阵。
2. 变量定义

int startX = 0, startY = 0;  // 每一圈的起始点,  初始时,它们的值是 0,表示从矩阵的左上角开始。
int offset = 1;//用来控制每一圈的边界(终止位置),初始为 1。
int count = 1;  // 矩阵中需要填写的数字,初始为 1,每填充一个元素,count 会加 1。
int loop = 1; // 用来记录当前处理的是第几圈,初始时是 1。
int i, j; // i 和 j 分别用于遍历矩阵的行和列。

  1. 外层 while 循环
while (loop <= n / 2) {

这个 while 循环控制的是每一圈的填充过程。
由于矩阵是螺旋状填充的,因此,每一圈的填充都会处理矩阵的一部分,直到处理完所有圈。
最大圈数为 n / 2(对于奇数 n,中间的点单独处理)。

**为什么最大圈数是 n / 2?**这是因为每次填充一个圈时,外层的边界会逐渐缩小。在每一圈结束后,内层的矩阵会变小,直到只剩一个单独的中心点(如果 n 是奇数的话)。因此,最大圈数 n / 2 控制了外层到内层的填充过程,保证了所有的圈都能够填满。
偶数大小的矩阵(如 n = 4):
对于一个 4 x 4 的矩阵,矩阵的结构可以分为两个圈:
外圈:包括第一行、最后一行、第一列和最后一列,填充完后剩下一个 2 x 2 的矩阵。
内圈:剩下的 2 x 2 的矩阵形成第二个圈,填充完后,矩阵完全填充。
所以,对于 n = 4,最大圈数为 4 / 2 = 2,表示两圈。
奇数大小的矩阵(如 n = 5):
对于一个 5 x 5 的矩阵,矩阵的结构可以分为两个完整的圈和一个中心点:
外圈:包括第一行、最后一行、第一列和最后一列,填充完后剩下一个 3 x 3 的矩阵。
内圈:剩下的 3 x 3 的矩阵形成第二个圈,填充完后剩下一个中心点(即 1 x 1 的矩阵)。
中心点:在第三圈完成填充后,唯一剩下的中心点填充最终的数字。
所以,对于 n = 5,最大圈数为 5 / 2 = 2(整数除法,向下取整),表示两圈,加上最后的中心点。

  1. 填充矩阵四个边
  1. 顶部
for (j = startY; j < n - offset; j++) {
    nums[startX][j] = count++;
}

填充当前圈的顶部,从 startY 开始,直到 n - offset,每次填充时,将当前 count 的值填入 nums[startX][j],然后 count++。
这个循环负责填充当前圈的顶部一行。
2) 右列
填充当前圈的右侧列,从 startX 到 n - offset,填充时逐行向下填充,依次填充 nums[i][j]。
这个循环负责填充当前圈的右侧一列。
3) 底部
填充当前圈的底部,从 j(当前列)开始向左逐步填充,直到回到 startY。
这个循环负责填充当前圈的底部一行。
4) 左列
填充当前圈的左侧列,从 i(当前行)开始,逐步向上填充,直到回到 startX。
这个循环负责填充当前圈的左侧一列。
5. 更新起始点和偏移量
每次填充完一个圈后,更新起始点 startX 和 startY,使得下一个圈从下一个内层位置开始。
同时,offset 增加 1,以便下一圈的填充范围变小。
loop 也加 1,表示处理了第 loop 圈。
6. 处理奇数大小矩阵的中心点

将一个 n x m 的城市区域分为两个子区域,使得这两个子区域的总价值之差最小。划分的方式可以是横向或纵向。
输入描述:
第一个整数 n 和 m,表示城市区域的大小,矩阵是 n x m 的。
接下来 n 行,每行有 m 个整数,表示每个区块的价值。
输出描述:
输出一个整数,表示两个子区域之间土地总价值之差的最小值。

1. 计算总土地价值
首先计算整个矩阵的总和,记作 total_sum。分配时我们可以在划分的两部分之间最小化价值差。
2. 前缀和计算
为了高效地计算任意区域的和,我们可以使用 前缀和。具体做法是
构建一个前缀和矩阵 prefix_sum,其中 prefix_sum[i][j] 表示从矩阵的左上角 (0, 0) 到 (i, j) 的区域总和。通过前缀和,可以在常数时间内计算任意子矩阵的和。
3. 按横向或纵向划分
我们可以尝试在每个可能的横向或纵向划分上计算两个子区域的总和差,并更新最小差值。
横向划分:按行划分,计算每个分割后的区域和。
纵向划分:按列划分,同样计算每个分割后的区域和。
4. 计算每种划分的差值
对于每个划分方式,计算两个子区域的总和差,然后找出最小的差值。
具体实现步骤
读取输入并构建矩阵。
使用前缀和计算区域和。
尝试所有横向和纵向划分方式,计算两个子区域的总和差。
输出最小的差值。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        
        // 读取矩阵的行列
        int n = sc.nextInt();
        int m = sc.nextInt();
        
        int[][] grid = new int[n][m];
        
        // 读取矩阵数据
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                grid[i][j] = sc.nextInt();
            }
        }
        
        // 计算总和
        int total_sum = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                total_sum += grid[i][j];
            }
        }
        
        // 计算前缀和
        int[][] prefix_sum = new int[n + 1][m + 1];
        
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                prefix_sum[i][j] = grid[i - 1][j - 1] + prefix_sum[i - 1][j] + prefix_sum[i][j - 1] - prefix_sum[i - 1][j - 1];
            }
        }
        
        // 存储最小差值
        int min_diff = Integer.MAX_VALUE;
        
        // 进行横向划分
        for (int i = 1; i < n; i++) {
            int top_sum = prefix_sum[i][m];  // top part (rows 0 to i-1, all columns)
            int bottom_sum = total_sum - top_sum;  // bottom part
            min_diff = Math.min(min_diff, Math.abs(top_sum - bottom_sum));
        }
        
        // 进行纵向划分
        for (int j = 1; j < m; j++) {
            int left_sum = prefix_sum[n][j];  // left part (all rows, columns 0 to j-1)
            int right_sum = total_sum - left_sum;  // right part
            min_diff = Math.min(min_diff, Math.abs(left_sum - right_sum));
        }
        
        // 输出最小差值
        System.out.println(min_diff);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值