2020/08/30第204场周赛
1569. 将子数组重新排序得到BST
这道题目首先需要找到关键点,再给定了根节点之后,第一个大于根节点和第一个小于根节点的数字必须是固定的,这决定了左右子树的根节点。因此我们知道,这个树除了根节点还具有n
个字节点,其中,大于根节点的也就是右子树有m
个点,因此我们可以选取
C
n
m
C_n^m
Cnm个位置放置,剩下的位置自然就是左子树。然后左右子树都还存在自身的排列,因此可以递归的求解。
注意这里求解组合数的方法利用了组合数的性质: C n m = C n − 1 m + C n − 1 m − 1 C_n^m = C_{n-1}^{m}+C_{n-1}^{m-1} Cnm=Cn−1m+Cn−1m−1。因此可以用dp的思想比较快速的求解。
这里其实可以作为一个模板:
private void helper(int n){
for (int i = 0; i<=n;i++){
for (int j = 0; j<=i;j++){
if (j == 0) C[i][j] = 1;
else C[i][j] = (int)((long)(C[i-1][j]+C[i-1][j-1])%mod);
}
}
}
题解:
class Solution {
int[][] C;
int mod = 1000000007;
public int numOfWays(int[] nums) {
int n = nums.length;
C = new int[n+2][n+2];
helper(n);
List<Integer> ans = new ArrayList<>();
for (int num:nums) ans.add(num);
return f(ans)-1;
}
private int f(List<Integer> list){
if (list.isEmpty()) return 1;
int n = list.size();
int bar = list.get(0);
List<Integer> left = new ArrayList<>();
List<Integer> right = new ArrayList<>();
for (int i = 1; i<n;i++){ // 这里需要抛出根节点
if (list.get(i)>bar) right.add(list.get(i));
else left.add(list.get(i));
}
// 这里要小心翼翼,防止爆int
return (int)((long)C[n-1][left.size()]*f(left)%mod * f(right)%mod);
}
// 递推的方法求解组合数
private void helper(int n){
for (int i = 0; i<=n;i++){
for (int j = 0; j<=i;j++){
if (j == 0) C[i][j] = 1;
else C[i][j] = (int)((long)(C[i-1][j]+C[i-1][j-1])%mod);
}
}
}
}
1567.乘积为正数的最长子数组
分析清楚题目可能的情况,三个情况:1. 偶数个负数;2.奇数个负数,因此我们需要保存第一个负数的位置,这个位置之后的区间是偶数个负数;3.出现0,全部清零,前面的都无效。
对于需要保存第一个负数位置的,采用栈的方法比较好。
class Solution {
public int getMaxLen(int[] nums) {
// 双指针,正数的情况,负数为偶数个,且无00
int ans = 0;
int n = nums.length;
int num = 0,pre = -1;
List<Integer> stack = new ArrayList<>();
for (int i = 0; i<n ;i++){
if (nums[i]<0) {
num++;
stack.add(i);
}
else if (nums[i] == 0){
num = 0;
pre = i;
stack.clear();
}
if (num%2 == 0) ans = Math.max(ans, i-pre);
else ans = Math.max(ans, i-stack.get(0));
}
return ans;
}
}