暴力求解两个for循环,会超时:
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int result = INT32_MAX; // 最终的结果
int sum = 0; // 子序列的数值之和
int subLength = 0; // 子序列的长度
for (int i = 0; i < nums.size(); i++) { // 设置子序列起点为i
sum = 0;
for (int j = i; j < nums.size(); j++) { // 设置子序列终止位置为j
sum += nums[j];
if (sum >= target) { // 一旦发现子序列和超过了s,更新result
subLength = j - i + 1; // 取子序列的长度
result = result < subLength ? result : subLength;
break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
}
}
}
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
return result == INT32_MAX ? 0 : result;
}
};
优化,滑动窗口——仅用一个for循环:
class Solution {
public:
int minSubArrayLen(int target, 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(sum>=target){
sublength=j-i+1;
result = result<sublength ? result : sublength;//取最小值
sum-=nums[i++];
}
}
return result==INT32_MAX? 0:result;
}
};
关键点:
1.只用一个循环,那么这个指针主要是用来定位末尾的,反过来想,若定位首指针,那没有意义,末尾就无法控制;
2.result一直取比较的更小值,预先定义成最大值;
3.选择while不是if,因为当进行一次减掉最前面的数值后,不一定就是最短子数组,需要继续重新判断,举例:[1,1,1,100] target=100,则最短子数组长度为1,但是如果使用if,则最短子数组长度为3;
【坑】看清题目,是大于等于target,不是只有等于,感觉不对劲也要再回去审题,避免时间浪费,可以先看这个代码最后实现什么功能,不用使劲往预期的题目上凑,读题习惯要改
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;//圈数
int mid=n/2;//n为奇数时,最中心的数组下标
int count=1;//标记元素顺序序号
int offset=1;//每圈的每侧遍历长度,采用左闭右开
int i,j;
while(loop--){
i=startx;
j=starty;
//上-右-下-左
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++;
}
//往里进一层
startx++;
starty++;
offset++;
}
if(n%2){
res[mid][mid]=count;
}
return res;
}
};
遵循循环不变量原则:区间的定义就是不变量。要在二分查找的过程中,保持不变量,就是在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则。
步骤:
最外层:
上侧,从左至右;右侧,从上至下;下侧,从右至左;左侧,从下至上;
接着往里进一层,重复上面操作。
在设置循环条件时,每个拐角的开闭要时刻保持一致,这样才能保证不混乱。下图选择左闭右开:
58. 区间和(第九期模拟笔试) (kamacoder.com)
若直接采用暴力解法,会超时,所以考虑提前存储每个位置及前缀之和
采用前缀和求区间和:
#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++){
cin>>vec[i];//scanf("%d",&vec[i]);
presum+=vec[i];
p[i]=presum;
}
//区间下标为闭区间
while(cin>>a>>b){ //~scanf("%d%d",&a,&b)
int sum;
if(a==0) sum=p[b];
else sum=p[b]-p[a-1];
cout<<sum<<endl;//printf("%d\n",sum);
}
}
优化:面对大量数据的读取输出操作,使用scanf 和 printf耗时更短
44. 开发商购买土地(第五期模拟笔试) (kamacoder.com)
和上题相同,采用前缀和:
#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 * 2));
}
int verticalCut = 0;
for (int j = 0; j < m; j++) {
verticalCut += vertical[j];
result = min(result, abs(sum - verticalCut * 2));
}
cout << result << endl;
}