leetcode刷题记录19(2023-08-30)【机器人的运动范围(bfs) | 剪绳子I(dp、数学) | 剪绳子II(乘法结果取余) | 二进制中1的个数(位运算、获取最右边的1)】

剑指 Offer 13. 机器人的运动范围

地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

示例 1:

输入:m = 2, n = 3, k = 1
输出:3

示例 2:

输入:m = 3, n = 1, k = 0
输出:1

提示:

1 <= n,m <= 100
0 <= k <= 20

从 (0, 0) 出发,每次向右和向下,注意,不需要向上和向左,因为只需要向右和向下就可以把整片地图遍历完成了。

#include<iostream>
#include<vector>
#include<queue>

using namespace std;

struct Postion {
    int x;
    int y;
};

class Solution {
public:
    int movingCount(int m, int n, int k) {
        vector<int> xDirection = { 0,1 };
        vector<int> yDirection = { 1,0 };
        vector<vector<bool>> vec(m, vector<bool>(n));
        queue<Postion> que;
        que.push({ 0,0 });
        vec[0][0] = true;
        int res = 0;
        while (!que.empty()) {
            Postion curPos = que.front();
            que.pop();
            int xPos = curPos.x;
            int yPos = curPos.y;
            int sum = 0;
            while (xPos != 0) {
                sum += xPos % 10;
                xPos /= 10;
            }

            while (yPos != 0) {
                sum += yPos % 10;
                yPos /= 10;
            }

            if (sum <= k) {
                res++;
                for (int i = 0; i < 2; i++) {
                    int newX = curPos.x + xDirection[i];
                    int newY = curPos.y + yDirection[i];
                    if (newX < 0 || newX >= m ||
                        newY < 0 || newY >= n || vec[newX][newY] == true) continue;
                    que.push({ newX,newY });
                    vec[newX][newY] = true;
                }
            }
        }
        return res;
    }
};

int main() {
    Solution sol;
    int res = sol.movingCount(2, 3, 1);
    cout << res;
    return 0;
}

剑指 Offer 14- I. 剪绳子

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

示例 1:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1

示例 2:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

提示:

2 <= n <= 58

利用动态规划的思想进行求解:dp[i] 表示 数字 i 被分成 j 个数的最大乘积,从 j=1,到 j=i-1 进行一遍统计,需要注意的是,我们是与2作为基准进行比较的,因此max函数的另一项参数为 j * (i - j)

class Solution {
public:
    int cuttingRope(int n) {
        
        vector<int> dp(n + 1);
        // 从2个数的乘积开始算
        for (int i = 2; i <= n; i++) {
            // dp[i] 表示 数字 i 被分成 j 个数的最大乘积
            for (int j = 1; j < i; j++) {
                dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j]));
            }
        }
        return dp[n];
    }
};

通过数学推导可以证明,一个数被拆分成3的个数越多,乘积就越大。

所以我们统计3的个数就可以了。同时,处理一下不能被3整除的情况,证明参考题解:https://leetcode.cn/problems/jian-sheng-zi-lcof/solutions/1794015/jian-sheng-zi-by-leetcode-solution-xku9/.

class Solution {
public:
    int cuttingRope(int n) {
        if (n <= 3) {
            return n - 1;
        }
        int num_3 = n / 3;
        int remain_3 = n % 3;
        if (remain_3 == 0) return pow(3, num_3);
        else if (remain_3 == 1) return pow(3, num_3 - 1) * 4;
        else return pow(3, num_3 - 1) * 6;
    }
};

这道题目充分诠释了,一流程序员靠数学,二流程序员靠算法。

剑指 Offer 14- II. 剪绳子 II

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m - 1] 。请问 k[0]k[1]…*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1

示例 2:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

提示:

2 <= n <= 1000

值得注意的是,由于结果超过了范围,无法保存,需要对中间结果进行取余,因此无法进行dp了,只能通过数学 推导 + 贪心 的做法。

主要就用到了下边的取余规则,两个数相乘,结果的余数,等于两个数分别取余然后相乘,结果再对 p 进行取余操作:
( x y ) ⊙ p = [ ( x ⊙ p ) ( y ⊙ p ) ] ⊙ p (xy)⊙p=[(x⊙p)(y⊙p)]⊙p (xy)p=[(xp)(yp)]p

这里参考了题解:https://leetcode.cn/problems/jian-sheng-zi-ii-lcof/solutions/106190/mian-shi-ti-14-ii-jian-sheng-zi-iitan-xin-er-fen-f/.

具体的求解思路和上别的第一道题目相同。

都是算数均值不等式得到推论,然后通过求导求极值,最后贪心求解。

class Solution {
public:
    int cuttingRope(int n) {
        if (n == 2) return 1;
        if (n == 3) return 2;
        if (n == 4) return 4;
        long long res = 1;
        int p = 1e9 + 7;
        while (n > 4) {
            res = (res * 3) % p;
            n -= 3;
        }
        res = (res * n) % p;
        return (int)res;
    }
};

剑指 Offer 15. 二进制中1的个数

编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为 汉明重量).)。

提示:

请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
在 Java 中,编译器使用 二进制补码 记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。

示例 1:

输入:n = 11 (控制台输入 00000000000000000000000000001011)
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 ‘1’。

示例 2:

输入:n = 128 (控制台输入 00000000000000000000000010000000)
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 ‘1’。

示例 3:

输入:n = 4294967293 (控制台输入 11111111111111111111111111111101,部分语言中 n = -3)
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 ‘1’。

提示:

输入必须是长度为 32 的 二进制串 。

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int res = 0;
        while(n!=0){
            res += n & 1;
            n = n >> 1;
        }
        return res;
    }
};

但这个代码有些粗糙,输入是一个uint32_t,也就是一个无符号数,如果有符号的怎么办呢?我们其实可以移动1的。

如果输入参数是有符号数:

class Solution {
public:
    int hammingWeight(int n) {
        int res = 0;
        int flag = 1;
        for(int i=0;i<32;i++){
            res += (n & flag) != 0 ? 1 : 0;
            flag = flag << 1;
        }
        return res;
    }
};

同时还可以根据以下规律进行优化:

n & ( n − 1 ) n \& (n-1) n&(n1)

上述这个操作会将n的最右边的1变为0,其它不变。

推导:

如果某个二进制数表示中最右边的1位于m位,那么减去1时,第m位由1变成0,而第m位之后的所有0变成1,整数中第m位之前的所有位都保持不变。

因此,我们通过这样的操作就进一步降低了循环的次数。

代码如下:

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int res = 0;
        while(n!=0){
            res++;
            n = n&(n-1);
        }
        return res;
    }
};
  • 25
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cherries Man

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值