Leetcode---353周赛

周赛题目

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;
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值