5404. 用栈操作构建数组
给你一个目标数组 target 和一个整数 n。每次迭代,需要从 list = {1,2,3…, n} 中依序读取一个数字。
请使用下述操作来构建目标数组 target :
Push:从 list 中读取一个新元素, 并将其推入数组中。
Pop:删除数组中的最后一个元素。
如果目标数组构建完成,就停止读取更多元素。
题目数据保证目标数组严格递增,并且只包含 1 到 n 之间的数字。
请返回构建目标数组所用的操作序列。
题目数据保证答案是唯一的。
示例 1:
输入:target = [1,3], n = 3
输出:[“Push”,“Push”,“Pop”,“Push”]
解释:
读取 1 并自动推入数组 -> [1]
读取 2 并自动推入数组,然后删除它 -> [1]
读取 3 并自动推入数组 -> [1,3]
示例 2:
输入:target = [1,2,3], n = 3
输出:[“Push”,“Push”,“Push”]
示例 3:
输入:target = [1,2], n = 4
输出:[“Push”,“Push”]
解释:只需要读取前 2 个数字就可以停止。
示例 4:
输入:target = [2,3,4], n = 4
输出:[“Push”,“Pop”,“Push”,“Push”,“Push”]
提示:
1 <= target.length <= 100
1 <= target[i] <= 100
1 <= n <= 100
target 是严格递增的
思路
若target
的长度为m
, 则时间复杂度O(m)
代码
class Solution {
public List<String> buildArray(int[] target, int n) {
ArrayList<String> ans = new ArrayList<>();
int m = target.length, end = target[m-1], i = 1, j = 0;
for (i=1; i<=end; ++i) {
if (target[j] == i) {
ans.add("Push");
++j;
} else {
ans.add("Push");
ans.add("Pop");
}
}
return ans;
}
}
5405. 形成两个异或相等数组的三元组数目
给你一个整数数组 arr 。
现需要从数组中取三个下标 i、j 和 k ,其中 (0 <= i < j <= k < arr.length) 。
a 和 b 定义如下:
a = arr[i] ^ arr[i + 1] ^ … ^ arr[j - 1]
b = arr[j] ^ arr[j + 1] ^ … ^ arr[k]
注意:^ 表示 按位异或 操作。
请返回能够令 a == b 成立的三元组 (i, j , k) 的数目。
示例 1:
输入:arr = [2,3,1,6,7]
输出:4
解释:满足题意的三元组分别是 (0,1,2), (0,2,2), (2,3,4) 以及 (2,4,4)
示例 2:
输入:arr = [1,1,1,1,1]
输出:10
示例 3:
输入:arr = [2,3]
输出:0
示例 4:
输入:arr = [1,3,5,7,9]
输出:3
示例 5:
输入:arr = [7,11,12,9,5,2,7,17,22]
输出:8
提示:
1 <= arr.length <= 300
1 <= arr[i] <= 10^8
思路
注意到若a = b
,则a ^ b = 0
,因此只要找到数组中连续若干个数,这些数的从左到右异或值为0即可
若arr
长度为n
,则时间复杂度O(n^2)
.
代码
class Solution {
public int countTriplets(int[] arr) {
int n = arr.length, i = 0, j = 0, xors = 0, ans = 0;
for (i=0; i<n; ++i) {
xors = 0;
for (j=i; j>=0; --j) {
xors = arr[j] ^ xors;
// System.out.println(j + " " + i + ": " + xors);
if (xors == 0) {
ans += i - j;
}
}
}
return ans;
}
}
5406. 收集树上所有苹果的最少时间
给你一棵有 n 个节点的无向树,节点编号为 0 到 n-1 ,它们中有一些节点有苹果。通过树上的一条边,需要花费 1 秒钟。你从 节点 0 出发,请你返回最少需要多少秒,可以收集到所有苹果,并回到节点 0 。
无向树的边由 edges 给出,其中 edges[i] = [fromi, toi] ,表示有一条边连接 from 和 toi 。除此以外,还有一个布尔数组 hasApple ,其中 hasApple[i] = true 代表节点 i 有一个苹果,否则,节点 i 没有苹果。
示例 1:
输入:n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], hasApple = [false,false,true,false,true,true,false]
输出:8
解释:上图展示了给定的树,其中红色节点表示有苹果。一个能收集到所有苹果的最优方案由绿色箭头表示。
示例 2:
输入:n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], hasApple = [false,false,true,false,false,true,false]
输出:6
解释:上图展示了给定的树,其中红色节点表示有苹果。一个能收集到所有苹果的最优方案由绿色箭头表示。
示例 3:
输入:n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], hasApple = [false,false,false,false,false,false,false]
输出:0
思路
递归。为每个节点维护一个值,如果左孩子/右孩子的值大于0,则当前节点的值加上左孩子/右孩子的值+1,否则如果左孩子/右孩子有苹果,则当前节点值加上1。递归之前先将边列表edges
转化为邻接表adj
,为了防止重复访问再使用一个数组vis
记录树节点是否访问过。
树的节点个数为n
,时间复杂度为O(n)
.
代码
class Solution:
def dfs(self, root, adj, hasApple, vis):
vis[root] = True
ans = 0
for child in adj[root]:
if not vis[child]:
childVal = self.dfs(child, adj, hasApple, vis)
if childVal > 0:
ans += childVal + 1
elif hasApple[child]:
ans += 1
# print('{}: {}'.format(root, ans))
return ans
def minTime(self, n: int, edges: List[List[int]], hasApple: List[bool]) -> int:
adj = [list() for _ in range(n)]
for edge in edges:
adj[edge[0]].append(edge[1])
adj[edge[1]].append(edge[0])
vis = [False] * n
return 2 * self.dfs(0, adj, hasApple, vis)
5407. 切披萨的方案数
给你一个 rows x cols 大小的矩形披萨和一个整数 k ,矩形包含两种字符: ‘A’ (表示苹果)和 ‘.’ (表示空白格子)。你需要切披萨 k-1 次,得到 k 块披萨并送给别人。
切披萨的每一刀,先要选择是向垂直还是水平方向切,再在矩形的边界上选一个切的位置,将披萨一分为二。如果垂直地切披萨,那么需要把左边的部分送给一个人,如果水平地切,那么需要把上面的部分送给一个人。在切完最后一刀后,需要把剩下来的一块送给最后一个人。
请你返回确保每一块披萨包含 至少 一个苹果的切披萨方案数。由于答案可能是个很大的数字,请你返回它对 10^9 + 7 取余的结果。
示例 1:
输入:pizza = [“A…”,“AAA”,"…"], k = 3
输出:3
解释:上图展示了三种切披萨的方案。注意每一块披萨都至少包含一个苹果。
示例 2:
输入:pizza = [“A…”,“AA.”,"…"], k = 3
输出:1
示例 3:
输入:pizza = [“A…”,“A…”,"…"], k = 1
输出:1
提示:
1 <= rows, cols <= 50
rows == pizza.length
cols == pizza[i].length
1 <= k <= 10
pizza 只包含字符 ‘A’ 和 ‘.’ 。
思路
动态规划。dp[row-1][col-1][cut-1]
表示pizza右下角的row * col
范围分成cut
份的方案数。
除了动态规划以外,一个核心点是判断左上顶点为(lx, ly)
,右下顶点为(rx, ry)
的区域内是否有苹果,方法是计算从左上角开始的前缀和preSum[row][col]
表示pizza的左上角的row * col
范围内的苹果数量,如此可以将"区域存在苹果判断"的时间复杂度降至O(mn)
(假设行数为m
,列数为n
,份数为k
).
总的时间复杂度是动态规划的时间复杂度,为O(mnk(m+n))
.
代码
class Solution {
private static final int mod = 1000000007;
private boolean containsApple(int lx, int ly, int rx, int ry, int[][] preSum) {
return preSum[rx+1][ry+1] + preSum[lx][ly] - preSum[lx][ry+1] - preSum[rx+1][ly] > 0? true: false;
}
public int ways(String[] pizza, int k) {
int m = pizza.length, n = pizza[0].length(), i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, cut = 0;
boolean[][][][] containsApple = new boolean[m][n][m][n];
int[][] preSum = new int[m+1][n+1]; // prefix sum from left-top
int[][][] dp = new int[m][n][k]; // dp from right-button
for (i1=1; i1<=m; ++i1) {
for (i2=1; i2<=n; ++i2) {
preSum[i1][i2] = preSum[i1-1][i2] + preSum[i1][i2-1] - preSum[i1-1][i2-1] + (pizza[i1-1].charAt(i2-1) == 'A'? 1: 0);
}
}
for (i1=0; i1<m; ++i1) {
for (i2=0; i2<n; ++i2) {
dp[i1][i2][0] = containsApple(m-1-i1, n-1-i2, m-1, n-1, preSum)? 1: 0;
for (i3=1; i3<k; ++i3) {
for (cut=0; cut<i1; ++cut) {
if (containsApple(m-1-i1, n-1-i2, m-1-i1+cut, n-1, preSum)) {
dp[i1][i2][i3] = (dp[i1][i2][i3] + dp[i1-cut-1][i2][i3-1]) % mod;
}
}
for (cut=0; cut<i2; ++cut) {
if (containsApple(m-1-i1, n-1-i2, m-1, n-1-i2+cut, preSum)) {
dp[i1][i2][i3] = (dp[i1][i2][i3] + dp[i1][i2-cut-1][i3-1]) % mod;
}
}
}
}
}
return dp[m-1][n-1][k-1];
}
}