代码随想录算法训练营Day2 | 209.长度最小的子数组 59.螺旋矩阵II 前缀和:区间和 开发商购买土地

Day2

209. Minimum Size Subarray Sum

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int ans = INT_MAX;
        int sum = 0;

        for(int left = 0, right = 0; right < nums.size(); ++right)
        {
            sum += nums[right];
            if(sum >= target)
            {
                ans = min(ans, right-left+1);
                while(left <= right)
                {
                    sum -= nums[left];
                    ++left;
                    if(sum >= target)
                        ans = min(ans, right-left+1);
                    else
                        break;
                }
            }
        }
        if(ans == INT_MAX)
            return 0;
        return ans;
    }
};

59. Spiral Matrix II

  • 解题代码就是对螺旋填入数字行为的模拟
  • 由于是螺旋填入,每侧(上下左右)都应该保持相同的填入准则
    • 比如每次填写,该行元素包含头不包含尾(即左闭右开)
    • 填写列的行为也应该与填写行一样
  • 最后考虑每转一圈,填写的起始位置和结束位置变化关系
    • 以上侧填行为例,我们向右填写。每转一圈后,开始位置向右移动一格,结束位置向左移动一格
class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        if(n == 1)
            return {{1}};
        vector<vector<int>> board(n, vector<int>(n, 0));
        int loop_num = n % 2 == 1 ? 2 * (n - 1) + 1 : 2 * n;
        int offset = 0;
        int row_start = 0;
        int col_start = 0;
        int num = 1;
        for(int cnt=0; cnt<loop_num; ++cnt)
        {
            // fill upper, to right
            for(int i=0+offset; i<n-offset-1; ++i)
            {
                board[0+offset][i] = num;
                ++num;
            }

            // fill right, to bottom
            for(int i=0+offset; i<n-offset-1; ++i)
            {
                board[i][n-offset-1] = num;
                ++num;
            }

            // fill bottom, to left
            for(int i=n-offset-1; i>0+offset; --i)
            {
                board[n-offset-1][i] = num;
                ++num;
            }

            // fill left, to up
            for(int i=n-offset-1; i>0+offset; --i)
            {
                board[i][0+offset] = num;
                ++num;
            }
            ++offset;
        }
        if(n % 2 == 1)
            board[n / 2][n / 2] = num;
        return board;
    }
};

区间和

  • 前缀和的典型应用,我们通过数组跟踪当前位置累加的和
    • 对于任意一个ipreSum[i]代表从开始到位置i的元素和,即sum{0, i}
    • 对任意一个区间[m, n],通过preSum计算就是sum{m, n} = preSum[n] - preSum[m-1]
      • 为什么不是preSum[n] - preSum[m]? 因为preSum[m]包含位置m的元素,不应该减去
#include <iostream>
#include <vector>

int main()
{
    int n;
    std::cin >> n;
    std::vector<int> preSum(n, 0);
    
    for(int i=0; i<preSum.size(); ++i)
    {
        int num;
        std::cin >> num;
        if(i == 0)
            preSum[i] = num;
        else
            preSum[i] = preSum[i-1] + num;
    }
    
    int a, b;
    while(std::cin >> a >> b)
    {
        if(a == 0)
            std::cout << preSum[b] << std::endl;
        else
            std::cout << preSum[b] - preSum[a-1] << std::endl;
    }
}

开发商购买土地

  • 一维前缀和的二维应用,重点在于土地是不可重复分割,且只能横向、纵向分割的
  • 这就表示我们最终也只有两种分割方式:横分和纵分。只不过分割的位置不同
  • 所以我们可以分别计算横分和的前缀和,然后取最小值,得到结果
    • 求前缀和时,我们将2D数组压缩成一维,原因是我们需要的是分割出来那部分的评分总和(土地的评分),而不在乎究竟这块地里哪个位置评分高
    • 取最小值时我们用了2*lsum 方便直接计算分割后两块地的差值
      • 假设lsum正好是把土地评分平分了,那么2*lsum=sum,我们的差值就是0
      • 否则我们就能得到一个分割后的评分差,然后取最小即可
#include <iostream>
#include <vector>
#include <climits>

using namespace std;

int main()
{
    int n, m;
    cin >> n >> m;
    
    vector<vector<int>> board(n, vector<int>(m, 0));
    int sum = 0;
    
    for(int i=0; i<n; ++i)
    {
        for(int j=0; j<m; ++j)
        {
            cin >> board[i][j];
            sum += board[i][j];
        }
    }
    
    vector<int> rowPre(n, 0);
    for(int i=0; i<n; ++i)
        for(int j=0; j<m; ++j)
            rowPre[i] += board[i][j];
    
    vector<int> colPre(m, 0);
    for(int i=0; i<m; ++i)
        for(int j=0; j<n; ++j)
            colPre[i] += board[j][i];
            
    int ans = INT_MAX;
    int lsum = 0;
    for(int i=0; i<rowPre.size(); ++i)
    {
        lsum += rowPre[i];
        ans = min(ans, abs(sum - 2*lsum));
    }
    
    lsum = 0;
    for(int i=0; i<colPre.size(); ++i)
    {
        lsum += colPre[i];
        ans = min(ans, abs(sum - 2*lsum));
    }
    cout << ans;
    return 0;
    
}
  • 8
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值