5、C++算法之代码随想录(数组)——前缀和

        前缀和在计算区间和这类题目时十分方便,因此,在学习算法时应当学习和掌握这种方法。

第一道:

(1)题目

        58. 区间和(第九期模拟笔试) (kamacoder.com)        (这种题的风格是ACM模式的)

给定一个整数数组 Array,计算该数组在每个指定区间内元素的总和。

输入描述

第一行输入为整数数组 Array 的长度 n,接下来 n 行,每行一个整数,表示数组的元素。随后的输入为需要计算总和的区间,直至文件结束。

输出描述

输出每个指定区间内元素的总和。

(2)思路

        给定一个大小为n的数组和两个下标,计算两个下标之间的区间和。这题看起来非常的简单。直接遍历下标值,累加返回即可。但是如果两个下标每次都是0和n-1,那是不是每次都要将整个数组累加一次。时间复杂度O(n*m) m为执行次数。时间复杂度并不小。因此,这里采用前缀和进行计算。

        前缀和的基本思想就是将数组的区间和存储起来,当使用时直接用大区间和减去小区间和即可。

(3)解题流程

        1.定义两个动态数组nums和p。

        2.对nums进行赋值操作,同时计算nums的区间和赋给p。例如p[i]表示nums数组从0到i的区间和。

        3. 输入要计算的区间(a,b)。如果a==0,直接输出p[b]。如果a!=0,输出p[b]-p[a-1]。

代码实现:

#include<iostream>
using namespace std;
#include<vector>
int main()
{
    int n=0,a=0,b=0,sum=0;
    cin>>n;
    vector<int> nums(n,0);
    vector<int> p(n,0);

    for(int i=0;i<n;i++) 
    {
       scanf("%d", &nums[i]);
        sum+=nums[i];
        p[i] = sum;
    }

    while(~scanf("%d%d", &a, &b))
    {
       
        if(a==0)  printf("%d\n",p[b]);
        else printf("%d\n",p[b]-p[a-1]);
    }
    return 0;
}

        使用cin和cout进行输入输出可能会超时,可以改用scanf和printf。效率更高点。

第二道:

(1)题目

        44. 开发商购买土地(第五期模拟笔试) (kamacoder.com) 

【题目描述】

在一个城市区域内,被划分成了n * m个连续的区块,每个区块都拥有不同的权值,代表着其土地价值。目前,有两家开发公司,A 公司和 B 公司,希望购买这个城市区域的土地。

现在,需要将这个城市区域的所有区块分配给 A 公司和 B 公司。

然而,由于城市规划的限制,只允许将区域按横向或纵向划分成两个子区域,而且每个子区域都必须包含一个或多个区块。

为了确保公平竞争,你需要找到一种分配方式,使得 A 公司和 B 公司各自的子区域内的土地总价值之差最小。

注意:区块不可再分。

【输入描述】

第一行输入两个正整数,代表 n 和 m。

接下来的 n 行,每行输出 m 个正整数。

输出描述

请输出一个整数,代表两个子区域内土地总价值之间的最小差距。

【输入示例】

3 3 1 2 3 2 1 3 1 2 3

【输出示例】

0

(2)思路

        这道题依旧是用前缀和的方式进行处理。将一块n*m的土地按横向或纵向进行划分,是两块土地的价值差最小。可以将两种划分方式分别进行计算,得到价值差最小的值,然后返回即可。

        以纵向划分为例,创建一个数组p,p[i]表示前 i 列的累加和。找到一个i值,使两部分价值差最小,等价于 p[m-1]-p[i-1]-p[i-1]。p[m-1]-p[i-1]表示i到m-1列的值,p[i-1] 表示0到i-1列的值。相减就是两块区间的价值差。找打最小的返回即可。横向划分的原理一样。

(3)解题流程

        1.定义大小为n*m二维数组将输入的值存储起来。

        2.定义个大小为m的数组存储列的累加和。

        3.定义个大小为n的数组存储行的累加和。

        4.遍历列数组,通过计算p[m-1]-p[i-1]-p[i-1]找到最小的价值差。(可能存在负数,取个绝对值)。

        5.遍历行数组,通过计算p[m-1]-p[i-1]-p[i-1]找到最小的价值差。

        6.返回最小的价值差。

代码实现:

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include <climits>
int main()
{  
    int n,m,minvalue=INT_MAX;
    cin>>n>>m;
    vector<vector<int>> nums(n,vector<int>(m,0));
    //输入数据
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            cin>>nums[i][j];
        }
    }
    int sum=0;
    //行求和
    vector<int> rowSum(n,0);

    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            sum+=nums[i][j];
        }
        rowSum[i] = sum;
    }
    sum=0;
    //列求和
    vector<int> colSum(m,0);
    for(int i=0;i<m;i++)
    {
        for(int j=0;j<n;j++)
        {
            sum+=nums[j][i];
        }
        colSum[i] = sum;
    }

    //列分隔
    for(int i=1;i<m;i++)
    {
        int q_value =colSum[m-1]-colSum[i-1];
        int value = abs(q_value-colSum[i-1]);
        minvalue = min(minvalue,value);
    }
    //行分割
    for(int i=1;i<n;i++)
    {
        int q_value =rowSum[n-1]-rowSum[i-1];
        int value = abs(q_value-rowSum[i-1]);
        minvalue = min(minvalue,value);
    }
    cout<<minvalue<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值