📜  前缀和数组–竞争性编程中的实现和应用(1)

📅  最后修改于: 2023-12-03 15:07:17.446000             🧑  作者: Mango

前缀和数组–竞争性编程中的实现和应用

前缀和数组是一种常用的数据结构,是数组的一种预处理方式。它的主要应用场景是统计一段连续区间的和,对于竞争性编程而言,前缀和数组也是一种强大的工具,通过它可以实现很多常见的问题,例如区间求和、区间奇偶判断等。

实现方式

前缀和数组的实现方式很简单,我们只需要将原数组中的元素依次累加,得到一个新的数组,新数组中的每一个元素就是原数组中前n个元素的和,其中n表示新数组的下标。代码如下:

vector<int> prefixSum(vector<int>& nums) {
    int n = nums.size();
    vector<int> sums(n + 1, 0);
    for (int i = 1; i <= n; i++) {
        sums[i] = sums[i - 1] + nums[i - 1];
    }
    return sums;
}
应用场景
区间求和

前缀和数组最常见的应用场景就是区间求和,这也是前缀和数组最初的设计初衷。对于原数组中任意一个区间[l, r],只需要用前缀和数组中下标为r+1的元素减去下标为l的元素即可得到区间[l, r]的和。代码如下:

int rangeSum(vector<int>& nums, int l, int r) {
    vector<int> sums = prefixSum(nums);
    return sums[r + 1] - sums[l];
}
区间奇偶判断

如果要判断一个数组中的任意一个区间[l,r]中奇数的个数是偶数还是奇数,可以用前缀和数组来实现。只需要用前缀和数组中下标为r+1的元素减去下标为l的元素,得到区间[l,r]中的元素个数k,如果k为偶数,则区间[l,r]中的奇数个数也为偶数,否则为奇数。代码如下:

bool isOddNumber(vector<int>& nums, int l, int r) {
    vector<int> sums = prefixSum(nums);
    return (sums[r + 1] - sums[l]) % 2 != 0;
}
子数组最大和

子数组最大和是一个经典的问题,在竞争性编程中也是一个常见的问题。可以用前缀和数组来解决这个问题,首先我们需要先计算出数组中的所有连续子数组的和,然后找出其中的最大值即可。代码如下:

int maxSubArray(vector<int>& nums) {
    int n = nums.size();
    vector<int> sums(n + 1, 0);
    for (int i = 1; i <= n; i++) {
        sums[i] = sums[i - 1] + nums[i - 1];
    }
    
    int ans = INT_MIN, minSum = 0;
    for (int i = 1; i <= n; i++) {
        ans = max(ans, sums[i] - minSum);
        minSum = min(minSum, sums[i]);        
    }
    return ans;
}
总结

前缀和数组是竞争性编程中很有用的一种数据结构,它可以帮助我们有效地解决很多问题,例如区间求和、区间奇偶判断、子数组最大和等问题。只需要对原数组进行一次预处理,就可以在O(1)的时间内回答很多问题,极大地提高了算法的效率。