算法----前缀和与差分

一,前缀和

1,一维前缀和:

以下是 C++ 中计算一维数组的前缀和的示例代码:

#include <iostream>
#include <vector>

using namespace std;

vector<int> prefixSum(vector<int>& arr) {
    int n = arr.size();
    vector<int> prefix(n);
    prefix[0] = arr[0];
    
    for (int i = 1; i < n; i++) {
        prefix[i] = prefix[i-1] + arr[i];
    }
    
    return prefix;
}

int main() {
    vector<int> arr = {1, 2, 3, 4, 5};
    vector<int> prefix = prefixSum(arr);
    
    for (int i = 0; i < prefix.size(); i++) {
        cout << prefix[i] << " ";
    }
    
    return 0;
}

输出结果为:1 3 6 10 15

这个示例代码定义了一个函数 prefixSum,输入参数为一个整数数组 arr,输出一个整数数组 prefix,存储了 arr 的前缀和。首先创建一个和 arr 大小相同的数组 prefix,将 arr 的第一个元素赋值给 prefix 的第一个元素。

然后使用遍历的方式计算 prefix 的其他元素。从索引 1 开始遍历 arr,依次计算每个元素的前缀和,即 prefix[i] = prefix[i-1] + arr[i]。这里使用了前一个元素的前缀和加上当前元素的值来计算当前元素的前缀和。

最后,在 main 函数中,定义了一个整数数组 arr,赋值为 1, 2, 3, 4, 5。调用 prefixSum 函数计算数组 arr 的前缀和,然后使用循环遍历输出 prefix 数组的元素,即得到了原数组 arr 的前缀和。

一个优美的算法:

S[i] = a[1] + a[2] + ... a[i]
a[l] + ... + a[r] = S[r] - S[l - 1]

2,二维前缀和:

二维前缀和是指对一个二维数组,计算出该数组中任意子矩阵的元素之和。可以利用动态规划的思想,通过预处理的方式计算出二维前缀和数组。

以下是C++代码示例:

#include <iostream>
#include <vector>

using namespace std;

// 计算二维前缀和
vector<vector<int>> computePrefixSum(vector<vector<int>>& matrix) {
    int m = matrix.size();
    int n = matrix[0].size();
    
    // 构造二维前缀和数组
    vector<vector<int>> prefixSum(m + 1, vector<int>(n + 1, 0));
    
    // 计算每个位置的前缀和
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            prefixSum[i][j] = prefixSum[i-1][j] + prefixSum[i][j-1] - prefixSum[i-1][j-1] + matrix[i-1][j-1];
        }
    }
    
    return prefixSum;
}

// 计算子矩阵的和
int computeSubMatrixSum(vector<vector<int>>& prefixSum, int row1, int col1, int row2, int col2) {
    return prefixSum[row2+1][col2+1] - prefixSum[row1][col2+1] - prefixSum[row2+1][col1] + prefixSum[row1][col1];
}

int main() {
    vector<vector<int>> matrix = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };
    
    vector<vector<int>> prefixSum = computePrefixSum(matrix);
    
    int subMatrixSum = computeSubMatrixSum(prefixSum, 0, 0, 1, 1);
    
    cout << "subMatrixSum: " << subMatrixSum << endl;
    
    return 0;
}

以上代码中,computePrefixSum函数用于计算二维前缀和数组,computeSubMatrixSum函数用于计算子矩阵的和。

在示例中,我们定义了一个3x3的二维数组matrix,然后通过computePrefixSum函数计算出二维前缀和数组prefixSum。接着使用computeSubMatrixSum函数计算出(0, 0)到(1, 1)的子矩阵的和。最后输出结果为subMatrixSum: 12

一个优美的算法:

S[i, j] = 第i行j列格子左上部分所有元素的和
以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1]

二,差分

C++差分是一种操作,它可以通过对原始数组进行一系列增减操作,从而实现快速地修改数组中某个区间内的元素值。

以下是C++代码示例:

#include <iostream>
#include <vector>

using namespace std;

// 对差分数组进行增减操作
void update(vector<int>& diff, int left, int right, int value) {
    diff[left] += value;
    if (right + 1 < diff.size()) {
        diff[right + 1] -= value;
    }
}

// 根据差分数组恢复原始数组
vector<int> restore(vector<int>& diff) {
    vector<int> nums(diff.size(), 0);
    nums[0] = diff[0];
    
    for (int i = 1; i < diff.size(); i++) {
        nums[i] = nums[i - 1] + diff[i];
    }
    
    return nums;
}

int main() {
    vector<int> nums = {1, 2, 3, 4, 5};
    
    vector<int> diff(nums.size(), 0);
    
    // 对差分数组进行操作,将区间[1, 3]内的元素都增加2
    update(diff, 1, 3, 2);
    
    // 恢复原始数组
    vector<int> restored = restore(diff);
    
    cout << "Original array:" << endl;
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    
    cout << "Restored array:" << endl;
    for (int num : restored) {
        cout << num << " ";
    }
    cout << endl;
    
    return 0;
}

以上代码中,我们首先定义了一个原始数组nums,然后创建一个和nums等长的差分数组diff,并将其初始化为全0。

接着,我们使用update函数对差分数组进行操作,将区间[1, 3]内的元素都增加2。更新后的差分数组为:[0, 2, 2, 2, 0]。

最后,我们使用restore函数根据差分数组恢复原始数组,并输出结果。

运行程序,输出结果如下:

Original array:
1 2 3 4 5 
Restored array:
1 4 5 6 5

可以看到,经过差分操作和恢复操作后,原始数组的区间[1, 3]内的元素都增加了2。

通过差分操作,我们可以在O(1)的时间复杂度内对原始数组进行一系列修改操作,而无需逐个修改原始数组的元素。这在某些算法问题中非常有用,能够提高代码的效率。

1,一维差分

C++一维差分可以通过对原始数组进行前缀和(或称为前缀和数组)的操作来实现。具体步骤如下:

  1. 创建一个与原始数组等长的差分数组diff,并将其初始化为全0。
  2. 计算原始数组的前缀和数组prefix_sum,其中prefix_sum[i]表示原始数组中前i个元素的和。
  3. 对差分数组进行操作:
    • 对于原始数组中的某个区间 [left, right] 内的元素,我们将差分数组中的 diff[left] 加上 valuediff[right+1] 减去 value。即:
      diff[left] += value;
      if (right + 1 < diff.size()) {
          diff[right + 1] -= value;
      }
      

  4. 根据差分数组恢复原始数组:
    • 创建一个与差分数组等长的数组restored,并将其初始化为全0。
    • 使用一个循环遍历差分数组,计算restored[i]的值:
      restored[i] = restored[i-1] + diff[i];
      

  5. 返回恢复后的原始数组restored

以下是C++代码示例:

#include <iostream>
#include <vector>

using namespace std;

vector<int> difference(vector<int>& nums) {
    int n = nums.size();
    vector<int> diff(n, 0);
    vector<int> prefix_sum(n, 0);

    // Step 2: Calculate prefix sum
    prefix_sum[0] = nums[0];
    for (int i = 1; i < n; i++) {
        prefix_sum[i] = prefix_sum[i - 1] + nums[i];
    }

    // Step 3: Update difference array
    diff[0] = nums[0];
    for (int i = 1; i < n; i++) {
        diff[i] = prefix_sum[i] - prefix_sum[i - 1];
    }

    return diff;
}

vector<int> restore(vector<int>& diff) {
    int n = diff.size();
    vector<int> restored(n, 0);

    // Step 4: Restore original array
    restored[0] = diff[0];
    for (int i = 1; i < n; i++) {
        restored[i] = restored[i - 1] + diff[i];
    }

    return restored;
}

int main() {
    vector<int> nums = {1, 3, 4, 2, 5};

    // Calculate difference array
    vector<int> diff = difference(nums);

    // Restore original array
    vector<int> restored = restore(diff);

    // Print original and restored arrays
    cout << "Original array: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;

    cout << "Restored array: ";
    for (int num : restored) {
        cout << num << " ";
    }
    cout << endl;

    return 0;
}

输出结果为:

Original array: 1 3 4 2 5 
Restored array: 1 3 4 2 5

可以看到,经过差分操作和恢复操作后,原始数组与恢复后的数组完全相同。

一个优美的算法:

给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1] -= c

2,二维差分

C++中,可以使用二维差分(Two-dimensional Difference)来求解二维矩阵区域的和或进行区域更新操作。下面是一个简单的示例代码,用于计算二维矩阵区域的和。

#include <iostream>
#include <vector>

using namespace std;

// 计算二维差分矩阵
vector<vector<int>> computeDifference(const vector<vector<int>>& matrix) {
    int rows = matrix.size();
    int cols = matrix[0].size();

    // 初始化二维差分矩阵
    vector<vector<int>> difference(rows, vector<int>(cols, 0));

    // 计算差分矩阵
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            if (i == 0 && j == 0) {
                difference[i][j] = matrix[i][j];
            } else if (i == 0) {
                difference[i][j] = matrix[i][j] - matrix[i][j-1];
            } else if (j == 0) {
                difference[i][j] = matrix[i][j] - matrix[i-1][j];
            } else {
                difference[i][j] = matrix[i][j] - matrix[i-1][j] - matrix[i][j-1] + matrix[i-1][j-1];
            }
        }
    }

    return difference;
}

// 计算二维矩阵区域的和
int computeSum(const vector<vector<int>>& matrix, int row1, int col1, int row2, int col2) {
    int rows = matrix.size();
    int cols = matrix[0].size();

    // 边界处理
    if (row1 < 0 || row1 >= rows || row2 < 0 || row2 >= rows || col1 < 0 || col1 >= cols || col2 < 0 || col2 >= cols) {
        return 0;
    }

    // 计算区域和
    int sum = matrix[row2][col2];
    if (row1 > 0) {
        sum -= matrix[row1-1][col2];
    }
    if (col1 > 0) {
        sum -= matrix[row2][col1-1];
    }
    if (row1 > 0 && col1 > 0) {
        sum += matrix[row1-1][col1-1];
    }

    return sum;
}

int main() {
    vector<vector<int>> matrix = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };

    vector<vector<int>> difference = computeDifference(matrix);
    int sum = computeSum(difference, 1, 1, 2, 2);

    cout << "Sum: " << sum << endl;

    return 0;
}

在上面的示例中,computeDifference函数用于计算二维差分矩阵,computeSum函数用于计算二维矩阵区域的和。可以根据需要修改输入矩阵和区域的起始、终止坐标,以适应不同的求解问题。

一个优美的算法:

给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c:
S[x1, y1] += c, S[x2 + 1, y1] -= c, S[x1, y2 + 1] -= c, S[x2 + 1, y2 + 1] += c
 

  • 11
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值