2.06-2.15力扣数学刷题

【1】326. 3 的幂

日期:2.06

1.题目链接:326. 3 的幂 - 力扣(LeetCode)https://leetcode.cn/problems/power-of-three/description/?envType=problem-list-v2&envId=math

2.类型:数学

3.方法一:试除法(一次题解)

循环条件:n && n % 3 == 0

n 不为 0(0 不是 3 的幂,且除以 3 无意义)

当前数能被 3 整除

循环体:n /= 3,去掉一个因子 3

循环结束后,检查 n == 1。如果成立,则原数是 3 的幂;否则不是。

关键代码:

cpp 复制代码
        while(n&&n%3==0){
            n/=3;
        }
        return n==1;

4.方法二:判断是否为最大 3 的幂的约数(一次题解)

在题目给定的 32 位有符号整数的范围内,最大的 3 的幂为 319=1162261467。只需要判断 n 是否是 319 的约数即可。

关键代码:

cpp 复制代码
 return n > 0 && 1162261467 % n == 0;

【2】343. 整数拆分

日期:2.07

1.题目链接:343. 整数拆分 - 力扣(LeetCode)https://leetcode.cn/problems/integer-break/description/?envType=problem-list-v2&envId=math

2.类型:数学,动态规划

3.方法一:动态规划(官方题解)

定义 dp[i] 表示将正整数 i 拆分成至少两个正整数之和所能得到的最大乘积。注意,i 从 2 开始,因为 1 无法拆分(至少两个数)。

对于每个 i,尝试将其拆分成两个部分:ji-j,其中 j 的取值范围是 1 到 i-1。对于每一对 (j, i-j),有两种可能性:

不再继续拆分 i-j,此时乘积为 j * (i - j)

继续拆分 i-j,即利用之前计算好的 dp[i-j],此时乘积为 j * dp[i - j]

取这两种情况的最大值,作为当前拆分方式下的候选值。遍历所有 j,更新当前 i 的最大值,即 dp[i] = max( max(j*(i-j), j*dp[i-j]) ) 对所有 j 取最大。最终 dp[n] 即为所求。、

关键代码:

cpp 复制代码
        vector<int> dp(n+1);          
        for(int i=2;i<=n;i++){  
            int curMax=0;
            for(int j=1;j<i;j++) { 
                // 比较不拆分 i-j 和拆分 i-j 两种情况
                curMax=max(curMax, max(j*(i-j),j*dp[i-j]));
            }
            dp[i] = curMax;              
        }

【3】57. 统计各位数字都不同的数字个数

日期:2.08

1.题目链接:357. 统计各位数字都不同的数字个数 - 力扣(LeetCode)https://leetcode.cn/problems/count-numbers-with-unique-digits/description/?envType=problem-list-v2&envId=math

2.类型:数学,组合数学

3.方法一:排列组合(半解)

该问题可以用组合数学直接求解,不需要遍历所有数字。分别统计所有位数小于等于 n 且各位数字互不相同的数的个数,然后累加。

一位数(0~9):共 10 个(包括 0)。

两位数:首位不能是 0(否则就是一位数),所以首位有 9 种选择(1~9),次位可以是 0~9 中除去首位已选的数字,有 9 种,共 9 × 9 = 81 个。

三位数:首位同样有 9 种,次位有 9 种,第三位有 8 种,共 9 × 9 × 8 = 648 个。

依此类推,对于 k 位数(2 ≤ k ≤ n),个数为:
9 × 9 × 8 × ... × (9 - k + 2),即 9 × P(9, k-1)(排列数)。

最终答案就是一位数个数加上所有 k 位数(k=2..n)的个数之和。

关键代码:

cpp 复制代码
        if(n==0){
            return 1;
        }
        if(n==1){
            return 10;
        }
        int ans=10;       
        int cur=9;        // cur 用于逐步计算 k 位数的个数,初始为 9 表示首位可选数
        for(int i=0;i<n-1;++i){
            cur*=(9-i); // 第 i+2 位数的个数:乘上当前位可选的数字个数(9,8,7,...)
            ans+=cur;     
        }
        return ans;

【4】342. 4的幂

日期:2.09

1.题目链接:342. 4的幂 - 力扣(LeetCode)https://leetcode.cn/problems/power-of-four/description/?envType=problem-list-v2&envId=math

2.类型:数学,位运算

3.方法一:二进制表示中 1 的位置(官方题解)

4 的幂首先一定是 2 的幂(因为 4^k = 2^{2k}),其次它的二进制表示中唯一的 1 必须出现在偶数位(从第 0 位开始计数)。例如:

1 的二进制 0001(第 0 位是 1,偶数位)

4 的二进制 0100(第 2 位是 1,偶数位)

16 的二进制 10000(第 4 位是 1,偶数位)

而其他 2 的幂(如 2、8、32...)的 1 在奇数位,不满足条件。

关键代码:

cpp 复制代码
return n > 0 && (n & (n - 1)) == 0 && (n & 0xaaaaaaaa) == 0;

【5】367. 有效的完全平方数

日期:2.10

1.题目链接:367. 有效的完全平方数 - 力扣(LeetCode)https://leetcode.cn/problems/valid-perfect-square/description/?envType=problem-list-v2&envId=math

2.类型:数学,二分查找

3.方法一:暴力(一次题解)

如果 num 为完全平方数,那么一定存在正整数 x 满足 x×x=num。于是从 1 开始,从小到大遍历所有正整数,寻找是否存在满足 x×x=num 的正整数 x。在遍历中,如果出现正整数 x 使 x×x>num,那么更大的正整数也不可能满足 x×x=num,不需要继续遍历了。

关键代码:

cpp 复制代码
        long x=1,square=1;        
        while(square<=num){          
            if(square==num){          
                return true;
            }
            ++x;                        
            square=x*x;               
        }
        return false;        

4.方法二:二分查找(一次题解)

考虑使用二分查找来优化方法二中的搜索过程。因为 num 是正整数,所以若正整数 x 满足 x×x=num,则 x 一定满足 1≤x≤num。于是将 1 和 num 作为二分查找搜索区间的初始边界。因为在移动左侧边界 left 和右侧边界 right 时,新的搜索区间都不会包含被检查的下标 mid,所以搜索区间的边界始终是没有检查过的。因此,当left=right 时,仍需要检查 mid=(left+right)/2。

关键代码:

cpp 复制代码
        int left=0,right=num;
        while(left<=right){
            int mid=(right-left)/2+left;
            long square=(long) mid*mid;
            if(square<num){
                left=mid+1;
            }else if (square>num){
                right=mid-1;
            }else{
                return true;
            }
        }

【6】371. 两整数之和

日期:2.11

1.题目链接:371. 两整数之和 - 力扣(LeetCode)https://leetcode.cn/problems/sum-of-two-integers/description/?envType=problem-list-v2&envId=math

2.类型:数学,位运算

3.方法一:位运算(官方题解)

a & b ------ 找出需要进位的位

按位与运算:对于每一位,只有 ab 同时为 1 时,该位才会产生进位。

(a & b) << 1 ------ 进位左移一位

进位需要加到更高一位上,所以左移一位。

注意:这里强制转换为 unsigned int 再左移,是为了避免有符号整数左移时的未定义行为(当符号位被左移时可能产生负数,但进位应当是无符号处理)。

a = a ^ b ------ 无进位加法

异或运算:对于每一位,ab 不同则为 1,相同则为 0。这恰好模拟了二进制加法不考虑进位的情况(0+0=0,0+1=1,1+0=1,1+1=0,但要产生进位)。

循环处理进位

将无进位和 a 与进位 carry 再次相加,直到进位为 0。

第一次循环后,a 变为无进位和,b 变为进位值。如果进位不为 0,说明还需要加一次,继续循环。最终当进位为 0 时,a 就是真正的和。

关键代码:

cpp 复制代码
    while(b!=0){                         
        unsigned int carry=(unsigned int)(a & b)<<1; 
        a=a^b;                             
        b=carry;                              // 将进位赋给 b,下一轮继续加
    }
    return a;                                   // 最终无进位,a 即为结果

【7】375. 猜数字大小 II

日期:2.12

1.题目链接:375. 猜数字大小 II - 力扣(LeetCode)https://leetcode.cn/problems/guess-number-higher-or-lower-ii/description/?envType=problem-list-v2&envId=math

2.类型:数学,动态规划

3.方法一:动态规划(半解)

如果区间只有一个数 i,那么不用猜,代价为 0(f[i][i] = 0)。

对于区间 [i, j]i < j),可以先猜一个数 ki ≤ k ≤ j),然后根据反馈:

如果猜对了(概率情况,但我们要考虑最坏情况),实际上考虑的是最坏情况,所以需要取两个分支中较大的代价。

如果猜的数字 k 比目标大,则下一步在 [i, k-1] 中猜,代价为 f[i][k-1]

如果猜的数字 k 比目标小,则下一步在 [k+1, j] 中猜,代价为 f[k+1][j]

本次猜错要支付金额 k,所以总代价为 k + max(f[i][k-1], f[k+1][j])

可以在所有可能的 k 中,选择使得这个最坏代价最小的那个,即:

复制代码
f[i][j] = min_{k ∈ [i,j]} { k + max(f[i][k-1], f[k+1][j]) }

关键代码:

cpp 复制代码
for(int i=n-1;i>=1;i--){
    for(int j=i+1;j<=n;j++){
        f[i][j]=j+f[i][j-1]; 
        for(int k=i;k<j;k++){
            f[i][j]=min(f[i][j], k+max(f[i][k-1],f[k+1][j]));
        }
    }
}

【8】390. 消除游戏

日期:2.13

1.题目链接:390. 消除游戏 - 力扣(LeetCode)https://leetcode.cn/problems/elimination-game/description/?envType=problem-list-v2&envId=math

2.类型:数学

3.方法一:等差数列模拟(半解)

每轮删除后,剩余数字个数减半(cnt >>= 1),间隔加倍(step <<= 1)。关键在于更新新的第一个数字 a1

从左到右删除(k % 2 == 0):第一个数字会被删除,新的第一个变为原来的第二个,即 a1 += step

从右到左删除(k % 2 == 1):第一个数字是否被删除取决于当前个数 cnt 的奇偶性:

cnt 为奇数,则第一个会被删除,新第一个为 a1 + step

cnt 为偶数,则第一个保留,a1 不变。

关键代码:

cpp 复制代码
while(cnt>1){
    if(k%2==0)a1+=step;                // 正向
    else a1=(cnt%2==0)?a1:a1+step; // 反向
    k++;
    cnt>>=1;   // 个数减半
    step<<=1;  // 步长加倍
}

【9】396. 旋转函数

日期:2.14

1.题目链接:396. 旋转函数 - 力扣(LeetCode)https://leetcode.cn/problems/rotate-function/description/?envType=problem-list-v2&envId=math

2.类型:数学,迭代

3.方法一:迭代(半解)

记数组 nums 的元素之和为 numSum。根据公式,可以得到:

F(0)=0×nums[0]+1×nums[1]+...+(n−1)×nums[n−1]
F(1)=1×nums[0]+2×nums[1]+...+0×nums[n−1]=F(0)+numSum−n×nums[n−1]

更一般地,当 1≤k<n 时,F(k)=F(k−1)+numSum−n×nums[n−k]。可以不停迭代计算出不同的 F(k),并求出最大值。

关键代码:

cpp 复制代码
        int f=0,n=nums.size();
        int numSum=accumulate(nums.begin(),nums.end(),0);
        for(int i=0;i<n;i++){
            f+=i*nums[i];
        }
        int res=f;  
        // 从最后一个元素开始,依次计算 F(1) 到 F(n-1)
        for(int i=n-1;i>0;i--){
            f+=numSum-n*nums[i];
            res=max(res, f);
        }
        return res;
相关推荐
喵呜嘻嘻嘻2 小时前
Gurobi求解器参数
java·数据结构·算法
产品经理邹继强2 小时前
VTC财务与投资篇②:预算革命——如何用三维算法决定每一分钱去哪
算法
Polaris北2 小时前
第二十四天打卡
算法
Anastasiozzzz3 小时前
G1垃圾回收流程详解
java·开发语言·算法
滴滴答滴答答3 小时前
LeetCode Hot100 之 17 有效的括号
算法·leetcode·职场和发展
掘根3 小时前
【C++STL】二叉搜索树(BST)
数据结构·c++·算法
老鼠只爱大米3 小时前
LeetCode经典算法面试题 #20:有效的括号(数组模拟法、递归消除法等五种实现方案详细解析)
算法·leetcode··括号匹配·数组模拟法·递归消除法
yxc_inspire3 小时前
2026年寒假牛客训练赛补题(五)
算法
不想看见4043 小时前
6.3Permutations -- 回溯法--力扣101算法题解笔记
笔记·算法·leetcode