螺旋矩阵
问题描述
给定一个正整数 nnn,生成一个包含从1到 n2n^2n2 的数字的矩阵,要求这些数字按顺时针顺序填充。
思路分析
螺旋矩阵的核心在于如何控制矩阵的填充顺序。具体来说,我们可以分步骤地按照“从左到右”,“从上到下”,“从右到左”,“从下到上”依次填充矩阵。在填充时,需要注意边界条件,即在每个方向填充时,不能超过已经填充的区域。
设矩阵大小为 n×nn \times nn×n,初始化上下左右四个边界分别为 top
, bottom
, left
, right
:
- 初始状态下,
top = 0
,bottom = n - 1
,left = 0
,right = n - 1
。 - 从左到右填充当前行后,
top
增加(下移一行)。 - 从上到下填充当前列后,
right
减少(左移一列)。 - 从右到左填充当前行后,
bottom
减少(上移一行)。 - 从下到上填充当前列后,
left
增加(右移一列)。 - 不断循环以上步骤,直到所有元素填充完毕。
代码实现
下面是用 C 语言编写的螺旋矩阵生成代码,并附有详细的注释说明:
#include <stdio.h>
#include <stdlib.h>
// 函数用于生成螺旋矩阵
int** generateMatrix(int n, int* returnSize, int** returnColumnSizes) {
// 初始化一个 n*n 的二维数组
int** matrix = (int**)malloc(n * sizeof(int*));
for (int i = 0; i < n; i++) {
matrix[i] = (int*)malloc(n * sizeof(int));
}
// 初始化上下左右四个边界
int top = 0, bottom = n - 1, left = 0, right = n - 1;
int num = 1; // 用于填充的数字,初始为1
while (num <= n * n) {
// 从左到右填充当前上边界所在行
for (int i = left; i <= right && num <= n * n; i++) {
matrix[top][i] = num++;
}
top++; // 上边界下移
// 从上到下填充当前右边界所在列
for (int i = top; i <= bottom && num <= n * n; i++) {
matrix[i][right] = num++;
}
right--; // 右边界左移
// 从右到左填充当前下边界所在行
for (int i = right; i >= left && num <= n * n; i--) {
matrix[bottom][i] = num++;
}
bottom--; // 下边界上移
// 从下到上填充当前左边界所在列
for (int i = bottom; i >= top && num <= n * n; i--) {
matrix[i][left] = num++;
}
left++; // 左边界右移
}
// 设置返回值
*returnSize = n;
*returnColumnSizes = (int*)malloc(n * sizeof(int));
for (int i = 0; i < n; i++) {
(*returnColumnSizes)[i] = n;
}
return matrix;
}
int main() {
int n = 3;
int returnSize;
int* returnColumnSizes;
int** matrix = generateMatrix(n, &returnSize, &returnColumnSizes);
// 打印生成的螺旋矩阵
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
// 释放分配的内存
for (int i = 0; i < n; i++) {
free(matrix[i]);
}
free(matrix);
free(returnColumnSizes);
return 0;
}
-
矩阵初始化:我们首先通过动态内存分配创建一个 n×nn \times nn×n 的二维数组
matrix
。 -
边界定义:定义上下左右四个边界
top
,bottom
,left
,right
,用于控制填充的区域。 -
填充循环:使用
while
循环填充数字。循环内部分为四步:- 从左到右:在当前
top
行,从left
到right
填充。 - 从上到下:在当前
right
列,从top
到bottom
填充。 - 从右到左:在当前
bottom
行,从right
到left
填充。 - 从下到上:在当前
left
列,从bottom
到top
填充。 - 每次完成填充后,更新相应边界。
- 从左到右:在当前
-
边界条件控制:使用条件
num <= n * n
确保填充不会超出矩阵大小。 -
结果返回:函数返回生成的矩阵,同时返回矩阵的大小信息。
-
内存管理:在使用完矩阵后,别忘了释放内存,防止内存泄漏。
通过上述步骤,我们实现了螺旋矩阵的生成,并且通过代码展示了其具体的实现细节。
“区间和”问题通常指的是在一个数组中计算特定区间的元素和。这在处理大量区间查询时非常常见,特别是在算法题中,比如前缀和、树状数组或线段树等方法都可以应用于区间和问题。
问题描述---区间和
给定一个整数数组和若干个区间查询,每个查询要求计算数组中某个区间内所有元素的和。
例如:
- 输入:数组
nums = [1, 2, 3, 4, 5]
和若干查询(i, j)
。 - 输出:每个查询
(i, j)
对应的区间[i, j]
内的元素和。
思路分析
为了提高效率,可以采用“前缀和”的方法。前缀和数组 prefixSum
是一个辅助数组,其中 prefixSum[i]
表示从数组起始位置到索引 i-1
的元素和。这可以帮助我们在 O(1)O(1)O(1) 时间内快速计算任意区间的和。
- 构造前缀和数组
prefixSum
:prefixSum[i] = nums[0] + nums[1] + ... + nums[i-1]
。 - 对于每个查询
(i, j)
,区间[i, j]
的和就可以通过prefixSum[j + 1] - prefixSum[i]
快速得到。
代码实现
以下是用 C 语言实现的区间和的代码,并附有详细的注释说明:
#include <stdio.h>
#include <stdlib.h>
// 构建前缀和数组
void buildPrefixSum(int* nums, int* prefixSum, int size) {
prefixSum[0] = 0; // 初始化第一个前缀和为0,表示空区间的和
for (int i = 1; i <= size; i++) {
prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
}
}
// 计算区间 [i, j] 的和
int rangeSum(int* prefixSum, int i, int j) {
return prefixSum[j + 1] - prefixSum[i];
}
int main() {
int nums[] = {1, 2, 3, 4, 5}; // 输入数组
int size = sizeof(nums) / sizeof(nums[0]); // 数组大小
// 动态分配前缀和数组
int* prefixSum = (int*)malloc((size + 1) * sizeof(int));
// 构建前缀和数组
buildPrefixSum(nums, prefixSum, size);
// 测试区间和计算
int i = 1, j = 3; // 查询区间 [1, 3]
int sum = rangeSum(prefixSum, i, j);
printf("The sum of the elements in the range [%d, %d] is: %d\n", i, j, sum);
// 释放分配的内存
free(prefixSum);
return 0;
}
详细讲解
-
前缀和数组构建:
prefixSum
数组用于存储从数组开始到某个位置的所有元素和。prefixSum[0]
被初始化为 0,表示空区间的和。- 通过迭代,逐步构建前缀和数组
prefixSum
,其中prefixSum[i]
表示从数组开始到索引i-1
的元素和。
-
区间和计算:
- 给定一个区间
[i, j]
,通过前缀和数组快速计算区间和。具体公式为prefixSum[j + 1] - prefixSum[i]
。 - 例如,计算区间
[1, 3]
的和,对应的公式为prefixSum[4] - prefixSum[1]
。
- 给定一个区间
-
主函数:
- 在主函数中,先定义输入数组
nums
并计算其大小。 - 动态分配前缀和数组
prefixSum
的内存。 - 调用
buildPrefixSum
函数生成前缀和数组。 - 测试区间
[i, j]
的和计算,并输出结果。 - 最后释放动态分配的内存。
- 在主函数中,先定义输入数组
总结
使用前缀和方法,可以在 O(1)O(1)O(1) 时间内快速回答区间和查询,代价是需要 O(n)O(n)O(n) 时间预处理数组并存储前缀和信息。这种方法非常适合在需要频繁查询区间和的情况下使用。
问题描述--开发商购买土地
【题目描述】
在一个城市区域内,被划分成了n * m个连续的区块,每个区块都拥有不同的权值,代表着其土地价值。目前,有两家开发公司,A 公司和 B 公司,希望购买这个城市区域的土地。
现在,需要将这个城市区域的所有区块分配给 A 公司和 B 公司。
然而,由于城市规划的限制,只允许将区域按横向或纵向划分成两个子区域,而且每个子区域都必须包含一个或多个区块。
为了确保公平竞争,你需要找到一种分配方式,使得 A 公司和 B 公司各自的子区域内的土地总价值之差最小。
注意:区块不可再分。
【输入描述】
第一行输入两个正整数,代表 n 和 m。
接下来的 n 行,每行输出 m 个正整数。
输出描述
请输出一个整数,代表两个子区域内土地总价值之间的最小差距。
要解决这个问题,我们需要通过横向或纵向的划分,将整个区域划分为两个子区域,并使得两个子区域的总价值之间的差距最小。
解决思路
-
累积求和:
- 首先,我们需要计算整个矩阵的总价值
total_sum
。 - 然后,我们可以通过逐行或逐列地累积求和来计算某个分割点的两个子区域的价值。
- 首先,我们需要计算整个矩阵的总价值
-
计算最小差值:
- 对于每个可能的划分方式,计算两个子区域的总价值,并计算这两个总价值之间的差值。
- 最后,选择所有差值中的最小值作为结果。
实现步骤
-
横向分割:
- 逐行累积求和,计算从第 1 行到第 i 行的子区域的总和
sum_top
。 - 另一个子区域的总和则为
total_sum - sum_top
。 - 计算这两个值的差,并更新最小差值。
- 逐行累积求和,计算从第 1 行到第 i 行的子区域的总和
-
纵向分割:
- 逐列累积求和,计算从第 1 列到第 j 列的子区域的总和
sum_left
。 - 另一个子区域的总和则为
total_sum - sum_left
。 - 计算这两个值的差,并更新最小差值。
- 逐列累积求和,计算从第 1 列到第 j 列的子区域的总和
代码实现
下面是 C 语言代码实现:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int min(int a, int b) {
return a < b ? a : b;
}
int main() {
int n, m;
scanf("%d %d", &n, &m);
int matrix[n][m];
int total_sum = 0;
// 输入矩阵并计算总和
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
scanf("%d", &matrix[i][j]);
total_sum += matrix[i][j];
}
}
int min_diff = INT_MAX;
// 计算横向分割的最小差值
for (int i = 0; i < n - 1; i++) { // 至少要分成两部分,所以只计算到n-1
int sum_top = 0;
for (int row = 0; row <= i; row++) {
for (int col = 0; col < m; col++) {
sum_top += matrix[row][col];
}
}
int sum_bottom = total_sum - sum_top;
int diff = abs(sum_top - sum_bottom);
min_diff = min(min_diff, diff);
}
// 计算纵向分割的最小差值
for (int j = 0; j < m - 1; j++) { // 至少要分成两部分,所以只计算到m-1
int sum_left = 0;
for (int col = 0; col <= j; col++) {
for (int row = 0; row < n; row++) {
sum_left += matrix[row][col];
}
}
int sum_right = total_sum - sum_left;
int diff = abs(sum_left - sum_right);
min_diff = min(min_diff, diff);
}
printf("%d\n", min_diff);
return 0;
}
详细讲解
-
输入矩阵并计算总和:
- 首先,我们读取输入的矩阵,并计算整个矩阵的总价值
total_sum
。
- 首先,我们读取输入的矩阵,并计算整个矩阵的总价值
-
计算横向分割的最小差值:
- 我们从第 1 行到第 i 行进行累积求和,得到
sum_top
,并通过total_sum - sum_top
计算sum_bottom
。 - 对于每个可能的分割点,计算
abs(sum_top - sum_bottom)
,并更新最小差值。
- 我们从第 1 行到第 i 行进行累积求和,得到
-
计算纵向分割的最小差值:
- 类似于横向分割,我们从第 1 列到第 j 列进行累积求和,得到
sum_left
,并通过total_sum - sum_left
计算sum_right
。 - 对每个可能的分割点,计算
abs(sum_left - sum_right)
,并更新最小差值。
- 类似于横向分割,我们从第 1 列到第 j 列进行累积求和,得到
-
输出最小差值:
- 最后,输出最小差值,即为两个子区域的总价值差值最小的情况。
总结
这个问题的关键在于通过遍历所有可能的分割线,并计算出最小的差值。通过横向和纵向的分割,可以得到最小的两个子区域之间的差值,从而保证了公平的分配。