剑指 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=[(x⊙p)(y⊙p)]⊙p
具体的求解思路和上别的第一道题目相同。
都是算数均值不等式得到推论,然后通过求导求极值,最后贪心求解。
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&(n−1)
上述这个操作会将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;
}
};