这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战
剑指 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
方法一
bfs:从{0,0}
开始宽搜,若搜到的点没有被遍历过,且符合各数位值相加<=k
,则加入队列。用dfs深搜也可以,不过dfs存在爆栈的隐患
class Solution {
Queue<int[]> q;
boolean[][] st;
int[] dx = {0,0,1,-1}, dy = {1,-1,0,0};
public int movingCount(int m, int n, int k) {
q = new LinkedList<int[]>();
st = new boolean[m][n];
return bfs(m, n, k);
}
public int bfs(int m, int n, int k) {
int res = 0;
q.add(new int[] {0, 0});
st[0][0] = true;
while (q.size() > 0) {
int[] t = q.poll();
int a = t[0], b = t[1];
res ++;
for (int i = 0; i < 4; i ++ ) {
int x = a + dx[i], y = b + dy[i];
if (x < 0 || x >= m || y < 0 || y >= n) continue;
if (!st[x][y]) {
int cnt = 0;
int t1 = x, t2 = y;
for (int j = 0; j < 3; j ++ ) {
cnt += (t1 % 10) + (t2 % 10);
t1 /= 10; t2 /= 10;
}
if (cnt <= k) {
q.add(new int[] {x, y});
}
st[x][y] = true;
}
}
}
return res;
}
}
时间复杂度: O(n)
空间复杂度: O(n)
剑指 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
方法一
数学方法:证明一可以参照这里,这里证明二参照y总的小学生证明法 :
假设把n拆分成了a1a2...*an是最优解,
- a中一定没有大于等于5的数,反证法:如果a中有,则该数可以拆分为
(a - 3) + 3
,而3(a - 3) > a
,当a等于4.5时 - a中一定没有1,因为拆分成
1 + (n - 1)
,乘积还没有不拆的大 - 综上所有的a一定在2、3、4中
- 而4又可以拆分为
2 + 2
,拆分前后是相等关系,所以最优解一定可以被若干个2、3所表示 - 且2最多为2个,假设有3个2,
2 + 2 + 2 = 6 = 3 + 3
,没有拆分为2个3大 - 因此最优解一定是可以被若干个3和最多2个2所表示
class Solution {
public int cuttingRope(int n) {
if (n <= 3) return 1 * (n - 1);
int res = 1;
res *= Math.pow(3, n / 3);
n %= 3;
if (n == 1) res = res / 3 * 4; // n=1,说明最后一个3可以和这个1组成2、2,所以把最后一个3除了,再乘个4
else if (n == 2) res *= 2;
return res;
}
}
时间复杂度: O(1)
空间复杂度: O(1)
注意: 题意为至少分成2段,所以当n <= 3
时,需要特判一下
方法二
动态规划:
- 状态定义
f[i]
表示将i拆分后的最大乘积 - 状态计算,以
f[i]
结尾一段来考虑,i
被分成了j
和i-j
,i-j
为靠尾一段,当i-j
固定时,j
可以拆,也可以不拆 j
不拆乘积就是i*(i - j)
,j
拆的话,乘积为(i - j) * f[j]
- 因此状态转移方程为
f[i] = max(f[i], (i - j) * max(j, f[j]))
class Solution {
public int cuttingRope(int n) {
int[] f = new int[n + 1];
f[1] = f[2] = 1;
for (int i = 3; i <= n; i ++ )
for (int j = 1; j < i; j ++ )
f[i] = Math.max(f[i], (i - j) * Math.max(f[j], j));
return f[n];
}
}
时间复杂度: O(n*n)
空间复杂度: O(n)