算法训练1-day2

36 阅读5分钟

今天的四道题是滑动窗口与前缀和

  1. 209. 长度最小的子数组 双指针构成滑动窗口,len初值为INT_MAX,最后注意判断一下是否有过更新就行 AC代码:
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int len = INT_MAX;
        int left = 0;
        int sum = 0;
        for (int right = 0; right < nums.size(); ++right) {
            sum += nums[right];
            if (sum >= target) {
                while (sum >= target) {
                    len = min(len, right - left + 1);
                    sum -= nums[left++];
                }

            }
        }
        return len == INT_MAX ? 0 : len;
    }
};

class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        int result = INT32_MAX;
        int sum = 0; // 滑动窗口数值之和
        int i = 0; // 滑动窗口起始位置
        int subLength = 0; // 滑动窗口的长度
        for (int j = 0; j < nums.size(); j++) {
            sum += nums[j];
            // 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件
            while (sum >= s) {
                subLength = (j - i + 1); // 取子序列的长度
                result = result < subLength ? result : subLength;
                sum -= nums[i++]; // 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
            }
        }
        // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
        return result == INT32_MAX ? 0 : result;
    }
};
  1. 59. 螺旋矩阵 II

模拟,注意每次循环最后还要将i和j移到正确的下一位置,另外注意n为奇数时,中间的空格需要额外处理

AC代码:

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        int len = n;
        int curr = 1;
        vector<vector<int>> ans(n, vector<int>(n));
        int i = 0, j = 0;
        while (curr < n * n) {
            // 从左往右
            for (j = n - len; j < len - 1; ++j) {
                ans[i][j] = curr++;
            }
            // 从下往上
            for (i = n - len; i < len - 1; ++i) {
                ans[i][j] = curr++;
            }
            // 从右往左
            for (; j > n - len; --j) {
                ans[i][j] = curr++;
            }
            // 从下往上
            for (; i > n - len; --i) {
                ans[i][j] = curr++;
            }
            len--;
            i++;
            j++;
        }
        if (n % 2 == 1) {
            ans[n / 2][n / 2] = curr;
        }
        return ans;
    }
};

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组
        int startx = 0, starty = 0; // 定义每循环一个圈的起始位置
        int loop = n / 2; // 每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
        int mid = n / 2; // 矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
        int count = 1; // 用来给矩阵中每一个空格赋值
        int offset = 1; // 需要控制每一条边遍历的长度,每次循环右边界收缩一位
        int i,j;
        while (loop --) {
            i = startx;
            j = starty;

            // 下面开始的四个for就是模拟转了一圈
            // 模拟填充上行从左到右(左闭右开)
            for (j; j < n - offset; j++) {
                res[i][j] = count++;
            }
            // 模拟填充右列从上到下(左闭右开)
            for (i; i < n - offset; i++) {
                res[i][j] = count++;
            }
            // 模拟填充下行从右到左(左闭右开)
            for (; j > starty; j--) {
                res[i][j] = count++;
            }
            // 模拟填充左列从下到上(左闭右开)
            for (; i > startx; i--) {
                res[i][j] = count++;
            }

            // 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
            startx++;
            starty++;

            // offset 控制每一圈里每一条边遍历的长度
            offset += 1;
        }

        // 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
        if (n % 2) {
            res[mid][mid] = count;
        }
        return res;
    }
};
  1. 58. 区间和(第九期模拟笔试)

前缀和的应用;C++ 代码 面对大量数据读取输出操作,最好用scanf 和 printf,耗时会小很多。

AC代码:

#include <iostream>
#include <vector>
using namespace std;
int main() {
  int n;
  cin >> n;
  vector < int > nums(n);
  vector<int> prefix(n + 1, 0);
  for (int i = 0; i < n; ++i) {
    cin >> nums[i];
    prefix[i + 1] = prefix[i] + nums[i];
  }

  int a, b;
  while (cin >> a >> b) {
    cout << prefix[b + 1] - prefix[a] << endl;
  }
  return 0;
}

//`scanf`函数返回成功读取的变量个数,
//如果遇到文件结束(End-of-File)或错误,则返回EOF(通常是-1)。  
//在Windows系统中,EOF通常被定义为-1,在Linux/Mac中也是-1。  
//而`~`运算符对-1(即二进制全1)取反会得到0(二进制全0),因此当`scanf`返回EOF时,`~scanf`就会变成0,循环条件为假,退出循环。
//所以,`while (~scanf("%d%d", &a, &b))` 
//等价于 `while (scanf("%d%d", &a, &b) != EOF)`。

#include <iostream>
#include <vector>
using namespace std;
int main() {
    int n, a, b;
    cin >> n;
    vector<int> vec(n);
    vector<int> p(n);
    int presum = 0;
    for (int i = 0; i < n; i++) {
        scanf("%d", &vec[i]);
        presum += vec[i];
        p[i] = presum;
    }

    while (~scanf("%d%d", &a, &b)) {
        int sum;
        if (a == 0) sum = p[b];
        else sum = p[b] - p[a - 1];
        printf("%d\n", sum);
    }
}
  1. 44. 开发商购买土地(第五期模拟笔试) 前缀和
#include <climits>
#include <iostream>
#include <vector>
using namespace std;

int main() {
	int n, m;
	cin >> n >> m;

	int sum = 0;
	vector<vector<int>> nums(n, vector<int>(m, 0));
	vector<int> horizontal_prefix(n + 1, 0);
	for (int i = 0; i < n; ++i) {
		horizontal_prefix[i + 1] = horizontal_prefix[i];
		for (int j = 0; j < m; ++j) {
		cin >> nums[i][j];
		horizontal_prefix[i + 1] += nums[i][j];
		sum += nums[i][j];
		}
	}

	vector<int> vertical_prefix(m + 1, 0);

	for (int j = 0; j < m; ++j) {
		vertical_prefix[j + 1] = vertical_prefix[j];
		for (int i = 0; i < n; ++i) {
		vertical_prefix[j + 1] += nums[i][j];
		}
	}

	int diff = INT_MAX;
	for (int i = 1; i < n; ++i) {
		diff = min(diff, abs(sum - 2 * horizontal_prefix[i]));
	}
	for (int j = 1; j < m; ++j) {
		diff = min(diff, abs(sum - 2 * vertical_prefix[j]));
	}

	cout << diff << endl;

	return 0;
}

#include <iostream>
#include <vector>
#include <climits>

using namespace std;
int main () {
    int n, m;
    cin >> n >> m;
    int sum = 0;
    vector<vector<int>> vec(n, vector<int>(m, 0)) ;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> vec[i][j];
            sum += vec[i][j];
        }
    }
    // 统计横向
    vector<int> horizontal(n, 0);
    for (int i = 0; i < n; i++) {
        for (int j = 0 ; j < m; j++) {
            horizontal[i] += vec[i][j];
        }
    }
    // 统计纵向
    vector<int> vertical(m , 0);
    for (int j = 0; j < m; j++) {
        for (int i = 0 ; i < n; i++) {
            vertical[j] += vec[i][j];
        }
    }
    int result = INT_MAX;
    int horizontalCut = 0;
    for (int i = 0 ; i < n; i++) {
        horizontalCut += horizontal[i];
        result = min(result, abs(sum - horizontalCut - horizontalCut));
    }
    int verticalCut = 0;
    for (int j = 0; j < m; j++) {
        verticalCut += vertical[j];
        result = min(result, abs(sum - verticalCut - verticalCut));
    }
    cout << result << endl;
}