给定一个含有
n
个正整数的数组和一个正整数target
。找出该数组中满足其总和大于等于target
的长度最小的子数组[numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回0
。
思路:采用滑动窗口的思想来解题
首先回答三个问题:
窗口里的内容是什么?
窗口的起始位置如何标识?
窗口的终止位置如何标识?
对于第一个问题,窗口里的内容是满足区间的和大于等于给定的target,这很好理解。
对于第二个问题,起始位置如何标识呢?如果是采用for循环的索引来标识起始位置,那终止位置的寻找需要又一个循环来嵌套,显然这种思路是暴力求解的办法,我们暂不谈论暴力求解的思路。
那么可以知道,for循环的索引标识的是窗口的终止位置,第三个问题解决了
对于起始位置,则需要另外想办法:
起始位置从起点出发,当与终点位置形成的区间子数组的和大于等于target时,记录下此时的子数组长度,然后起始位置开始往后面移动,当又满足子数组和大于等于target时,再次记下子数组的长度,并且和之前的长度比较取最短,如此往复,便能得到最小值。
这里有人会问了,循环又嵌套循环,难道时间复杂度不是和暴力解法相似吗?
其实,不一定循环嵌套循环就必然是O(n^2)甚至更大,大体框架来看是一个大的for循环,而while循环里的元素进来和出去个依一次,因此时间复杂度可以简单记为2*n,即O(n),因此这是合理的。
int minSubArrayLen(int target, vector<int>& nums) {
int sum = 0; //最小子数组和
int result = nums.size() + 1; //最终的子数组长度
int sublength = 0; //子数组长度
int i = 0; //起始位置
for(int j = 0; j < nums.size(); j ++){
sum += nums[j];
while(sum >= target){
sublength = j - i + 1;
result = result < sublength ? result : sublength;
sum -= nums[i++];
}
}
return result == nums.size() + 1 ? 0 : result;
}
给你一个正整数
n
,生成一个包含1
到n2
所有元素,且元素按顺时针顺序螺旋排列的n x n
正方形矩阵matrix
。
思路:
这其实没有涉及到什么算法思想,重要的是考察代码能力,能够对该过程进行模拟,从而使得题目能够得到解决。
在模拟过程中需要注意区间的选择,从左到右,从上到下,从右到左,从下到上,四个方向需要遵从同样的区间规则,左闭右闭或者左闭右开,此外,还要注意循环圈数、偏移量、当n为奇数时最中间的值需要额外赋值等问题。
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> matric(n, vector<int>(n, 0)); //创建二维数组
int loop = n / 2; //循环圈数
int mid = n /2; //主要是当n为奇数时需要对中间额外进行赋值
int offset = 1; //设置偏移值,这里选择左闭有开原则;
int count = 1; //为每个空格赋值
int startx = 0, starty = 0; //设置起始坐标
int i, j;
while(loop --){
i = startx;
j = starty;
//从左到右
for(j; j < n - offset; j ++){
matric[i][j] = count ++;
}
//从上到下
for(i; i < n - offset; i ++){
matric[i][j] = count ++;
}
//从右到左
for(; j > offset - 1; j --){
matric[i][j] = count ++;
}
//从下到上
for(; i > offset - 1; i --){
matric[i][j] = count ++;
}
startx ++;
starty ++; //每循环一圈起始点坐标都需要加1
offset ++; //每循环一圈偏移量需要加1
}
if(n % 2){
matric[mid][mid] = count;
}
return matric;
}
58.区间和
题目描述
给定一个整数数组 Array,请计算该数组在每个指定区间内元素的总和。
输入描述
第一行输入为整数数组 Array 的长度 n,接下来 n 行,每行一个整数,表示数组的元素。随后的输入为需要计算总和的区间下标:a,b (b > = a),直至文件结束。
输出描述
输出每个指定区间内元素的总和。
输入示例
5 1 2 3 4 5 0 1 1 3
输出示例
3 9
思路:采用前缀和的思想求解问题
一般我们拿到这道题就直接先将输入数据存入数组中,然后再从给定区间a、b来累加求和,这样确实简单。
但,当我们查询次数为m,查询区间为0~n-1时,时间复杂度为O(m*n),当m越来越大时,时间复杂度越来越大,会显示时间超时。
因此我们不能采用上面的方法,而应该采用前缀和的方式。
简单来说就是重新创建一个数组,将原数组0到n的和保存在对应位置,比如第一个位置存储区间0到0(也就是第一个数),第二个位置存储区间0到1(第1、第2个数),.......,以此类推,当我们要计算某个区间的时候,直接取对应区间的数即可,比如区间a、b,就等于数组s[b]-s[a-1],当然需要分情况讨论一下,a为0和不为0,以及对应下标是否需要减一,这里就不推导了。
#include<iostream>
#include<vector>
using namespace std;
int main(){
int n;
cin>>n;
vector<int> arr(n);
vector<int> s(n);
int sum = 0;
for(int i = 0; i < n; i ++){
scanf("%d", &arr[i]);
sum += arr[i];
s[i] = sum;
}
int a, b;
while(~scanf("%d%d", &a, &b)){ //等价于while(scanf("%d%d", &a, &b) != EOF)
int count;
if(a == 0){
count = s[b];
}else{
count = s[b] - s[a - 1];
}
printf("%d\n", count);
}
}
44.开发商购买土地
题目描述
在一个城市区域内,被划分成了n * m个连续的区块,每个区块都拥有不同的权值,代表着其土地价值。目前,有两家开发公司,A 公司和 B 公司,希望购买这个城市区域的土地。
现在,需要将这个城市区域的所有区块分配给 A 公司和 B 公司。
然而,由于城市规划的限制,只允许将区域按横向或纵向划分成两个子区域,而且每个子区域都必须包含一个或多个区块。 为了确保公平竞争,你需要找到一种分配方式,使得 A 公司和 B 公司各自的子区域内的土地总价值之差最小。
注意:区块不可再分。
输入描述
第一行输入两个正整数,代表 n 和 m。
接下来的 n 行,每行输出 m 个正整数。
输出描述
请输出一个整数,代表两个子区域内土地总价值之间的最小差距。
输入示例
3 3 1 2 3 2 1 3 1 2 3
输出示例
0
提示信息
如果将区域按照如下方式划分:
1 2 | 3
2 1 | 3
1 2 | 3两个子区域内土地总价值之间的最小差距可以达到 0。
数据范围:
1 <= n, m <= 100;
n 和 m 不同时为 1。
思路:这道题可以沿用上面讲到的区间和的方法。
题目中说“只允许将区域按横向或纵向划分成两个子区域”,因此思路很清晰,首先需要按纵向和横向分别求土地价值最小,然后比较再取最小。
借鉴区间和的思想,则以横向为例,对某一行的土地价值分别求和后,形成一个大小为行数的另外一个数组,对该数组的数据采用逐个累加并且逐个比较,也就是说,加了一个后,相当于是求了一行的数据,也就将所有土地分成了被加的一块和剩余的一块总共两块区域,此时进行两块土地的价值做差并求绝对值,得到两块土地总价值之差,但是还不能确定是最小,因此还需要继续加,继续分。
行弄完后再弄列,所有弄完后进行比较,取最小值即算完成。
当然还有一种方式,就是不采用区间和的方式,对每一行进行累加后就进行判断,取最小,对列也是如此,到最后也就能得出最小的土地价值差。
这两种方法的时间复杂度均为O(n^2)
#include<iostream>
#include<vector>
#include<climits>
using namespace std;
int main(){
int n, m;
cin>>n>>m;
int sum = 0;
int result = INT_MAX;
vector<vector<int>> arr(n, vector<int>(m, 0));
for(int i = 0; i < n; i ++){
for(int j = 0; j < m; j ++){
//scanf("%d", &arr[i][j]);
cin>>arr[i][j];
sum += arr[i][j];
}
}
//横向统计权值
int horizontalCut = 0;
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j ++){
horizontalCut += arr[i][j];
}
result = min(result, abs(sum - horizontalCut - horizontalCut));
}
//纵向统计权值
int verticalCut = 0;
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
verticalCut += arr[j][i];
}
result = min(result, abs(sum - verticalCut -verticalCut));
}
cout<<result<<endl;
}