周赛题目
2769. 找出最大的可达成数字
2770. 达到末尾下标所需的最大跳跃次数
2771. 构造最长非递减子数组
2772. 使数组中的所有元素都等于零
一、找出最大的可达成数字
这题就是简单的不能在简单的简单题, 题目意思是:给你一个数num和操作数t,需要你在对num和x进行t次操作之后,使得x<=num,问这个初始x的最大值是多少?那很显然num不断+1,x不断-1,最后x==num这种情况下,x的初始值最大,代码如下
int theMaximumAchievableX(int num, int t){
return num+2*t;
}
二、达到末尾下标所需的最大跳跃次数
遇到这种从数组头跳到尾的跳跃问题,一般都是用动态规划,但是我们先不用动态规划,我们先用递归来想一想
1.确定递归参数及其返回值:根据题目,它要求从0到n的最大跳跃次数,我们就定义dfs(i)返回从0到i的最大跳跃次数(i表示下标)
2.确定递归的表达式:dfs(i)=max{dfs(k,i)+1} (k<i),条件是abs(nums[i]-nums[k])<=target)
3.确定递归的边界边界和递归入口:
递归边界:i==0时,返回0,即理解为在一开始的跳跃次数是0,或者从0到0不需要跳跃
递归入口:dfs(n-1)
代码如下
//这个是一般的递归写法,会超时
int T;
int*Nums;
int dfs(int i){
if(i==0)
return 0;
int res=INT_MIN;//这里给整形的最小值表示一种不可能的可能性,且不防止后面取较大值
for(int k=0;k<i;k++){
if(abs(Nums[k]-Nums[i])<=T){
res=fmax(res,dfs(k)+1);
}
}
return res;
}
int maximumJumps(int* nums, int numsSize, int target){
Nums=nums;
T=target;
int ans=dfs(numsSize-1);
return ans<0?-1:ans;
}
//记忆化搜索
int T;
int*Nums;
int*memo;
int dfs(int i){
if(i==0)
return 0;
if(memo[i]!=INT_MAX)
return memo[i];
int res=INT_MIN;
for(int k=0;k<i;k++){
if(abs(Nums[k]-Nums[i])<=T){
res=fmax(res,dfs(k)+1);
}
}
return memo[i]=res;
}
int maximumJumps(int* nums, int numsSize, int target){
Nums=nums;
T=target;
memo=(int*)malloc(sizeof(int)*numsSize);
for(int i=0;i<numsSize;i++)
memo[i]=INT_MAX;
int ans=dfs(numsSize-1);
free(memo);
return ans<0?-1:ans;
}
//1:1翻译成递推
//f[i]代表到i位置所需要的最大的跳跃次数
int maximumJumps(int* nums, int numsSize, int target){
int f[numsSize];
f[0]=0;
for(int i=1;i<numsSize;i++){
int res=INT_MIN/2;
for(int j=0;j<i;j++){
if(abs(nums[i]-nums[j])<=target){
res=fmax(res,f[j]+1);
}
}
f[i]=res;
}
return f[numsSize-1]<0?-1:f[numsSize-1];
}
那么如果我们一开始就知道用动态规划,我们应该怎么思考这道题?其实思考的过程和上面的递归神似,我们只要思考下面的三个问题:1.动规的数组的含义是什么?2.动规的递推表达式是什么?3.动规数组的初始化是什么?
参开上面代码我们不难回答上面三个问题:
1.f[i] 代表从0到i所需的最大跳跃次数
2.f[i] = max{ f[k] + 1 } ( k < i ) 条件为abs( f[i] - f[k] ) <= target
3.f[0] = 0
代码就是上面的由递归翻译出的递推
大家可以自己去感悟感悟两种思考过程的联系和区别,对以后的做题还是很有帮助的
三、构造最长非递减子数组
其实这题和上面一题一样用递归来解决,我们还是需要(从后往前推)
1.确定递归的参数和返回值:我们需要确定第i个数字取nums1[i]还是nums2[i],所以我们需要两个参数,dfs(i,0/1),i代表当前位下标,0/1代表第i个数选的是哪个数组中的数,返回值代表子数组的最大长度
2.确定递归的表达式:dfs(i,0/1)=max{dfs(i-1,0/1)+1} 前提:符合条件
3.递归的边界和入口:边界条件:i==0,返回1
代码如下
int **nums;
int*Nums1;
int*Nums2;
int**memo;
int dfs(int i,int j){
if(i==0)
return 1;
if(memo[i][j]!=-1)
return memo[i][j];
int res=1;
if(Nums1[i-1]<=nums[j][i])
res=fmax(res,dfs(i-1,0)+1);
if(Nums2[i-1]<=nums[j][i])
res=fmax(res,dfs(i-1,1)+1);
return memo[i][j]=res;
}
int maxNonDecreasingLength(int* nums1, int nums1Size, int* nums2, int nums2Size){
nums=(int**)malloc(sizeof(int*)*2);
nums[0]=nums1,nums[1]=nums2;
Nums1=nums1,Nums2=nums2;
memo=(int**)malloc(sizeof(int*)*nums1Size);
for(int i=0;i<nums1Size;i++){
memo[i]=(int*)malloc(sizeof(int)*2);
memo[i][0]=memo[i][1]=-1;
}
int ans=1;
for(int i=0;i<nums1Size;i++){
ans=fmax(fmax(dfs(i,0),dfs(i,1)),ans);
}
for(int i=0;i<nums1Size;i++)
free(memo[i);
free(memo);
return ans;
}
//1:1翻译成递推
int maxNonDecreasingLength(int* nums1, int nums1Size, int* nums2, int nums2Size){
int**nums=(int**)malloc(sizeof(int*)*2);
nums[0]=nums1,nums[1]=nums2;
int f[nums1Size][2];
f[0][0]=f[0][1]=1;
int ans=1;
for(int i=1;i<nums1Size;i++){
for(int j=0;j<2;j++){
int res=1;
if(nums1[i-1]<=nums[j][i])
res=fmax(res,f[i-1][0]+1);
if(nums2[i-1]<=nums[j][i])
res=fmax(res,f[i-1][1]+1);
f[i][j]=res;
ans=fmax(ans,f[i][j]);
}
}
return ans;
}
//优化空间复杂度
int maxNonDecreasingLength(int* nums1, int nums1Size, int* nums2, int nums2Size){
int**nums=(int**)malloc(sizeof(int*)*2);
nums[0]=nums1,nums[1]=nums2;
int f[2][2];
f[0][0]=f[0][1]=1;
int ans=1;
for(int i=1;i<nums1Size;i++){
for(int j=0;j<2;j++){
int res=1;
if(nums1[i-1]<=nums[j][i])
res=fmax(res,f[(i-1)%2][0]+1);
if(nums2[i-1]<=nums[j][i])
res=fmax(res,f[(i-1)%2][1]+1);
f[i%2][j]=res;
ans=fmax(ans,f[i%2][j]);
}
}
return ans;
}
四、使数组中的所有元素都等于零
这题看到对一段区间进行加减的操作就要想到差分数组,那么差分数组是啥?下面举个例子来带领大家了解一下
了解了差分数组之后,这题其实就迎刃而解了,补充一点当差分数组全为0时,原数组就是全0
代码如下
bool checkArray(int* nums, int numsSize, int k){
int f[numsSize+1];//防止x下面的i+k越界
memset(f,0,sizeof(f));
f[0]=nums[0];
for(int i=1;i<numsSize;i++)
f[i]=nums[i]-nums[i-1];
f[numsSize]=-nums[numsSize-1];//最后一个数之后的值不存在我们就当在数组后面加了一个元素0
for(int i=0;i+k<=numsSize;i++){
if(f[i]>0){
f[i+k]+=f[i];
f[i]=0;
}
}
for(int i=0;i<=numsSize;i++){
if(f[i]!=0)
return false;
}
return true;
}