1658. Minimum Operations to Reduce X to Zero

1658. Minimum Operations to Reduce X to Zero

Question

You are given an integer array nums and an integer x. In one operation, you can either remove the leftmost or the rightmost element from the array nums and subtract its value from x. Note that this modifies the array for future operations.

Return *the minimum number of operations to reduce x to exactly 0 if it is possible, otherwise, return *-1.

Solution

将原有问题的从两边减去一定的值,寻找加和为x的问题转换为寻找滑动窗口中的总和为total - x的新问题。

滑动窗口

初始化当前窗口内的sum,左侧指针left与右侧指针right。
每次将一个新的元素nums[right]加入窗口范围。

当当前的sum大于寻找的target,则将nums[left]滑出窗口,更新left与sum的值。

如果当前窗口内的sum等于寻找的target,则更新记录最小操作次数的min。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public int minOperations(int[] nums, int x) {
int total = 0, min = Integer.MAX_VALUE;
for(int num : nums){
total += num;
}
int target = total - x; //将减去两侧元素并寻找和为x的结果的问题转换为用滑动窗口寻找total - x的结果。
int sum = 0, left = 0, right = 0;
while(right < nums.length){
sum += nums[right]; //调整右侧范围,加入新元素到窗口
right++;

while(left < right && sum > target){ //如果窗口内的和大于目标,则调整左侧范围
sum -= nums[left];
left++;
}
if(sum == target){ //如果窗口内的和等于目标,则更新最小操作次数
min = Math.min(min, nums.length - (right - left));
}
}
return min == Integer.MAX_VALUE ? -1 : min;
}
}
1332. Remove Palindromic Subsequences

1332. Remove Palindromic Subsequences

Question

You are given a string s consisting only of letters 'a' and 'b'. In a single step you can remove one palindromic subsequence from s.

Return the minimum number of steps to make the given string empty.

A string is a subsequence of a given string if it is generated by deleting some characters of a given string without changing its order. Note that a subsequence does not necessarily need to be contiguous.

A string is called palindrome if is one that reads the same backward as well as forward.

Solution

当字符为空时,返回0。
注意本题中的子序列不需要连续。
由于只有’a’与’b’两个字符,因此最多两步即可将字符串清空。

辅助方法判断字符串是否为回文字符串。
如果为真则返回1,只需要删除一次。

如果为假则返回2,需要先删除所有的’a’,再删除所有的’b’。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public int removePalindromeSub(String s) {
if(s.length() == 0) return 0;
return isPalindrome(s) ? 1 : 2;
}

private boolean isPalindrome(String s){
int i = 0, j = s.length() - 1;
while(j > i){
if(s.charAt(i) != s.charAt(j)) return false;
i++;
j--;
}
return true;
}
}
52. N-Queens II

52. N-Queens II

Question

The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other.

Given an integer n, return the number of distinct solutions to the n-queens puzzle.

Solution

和昨天的题目51. N-Queens一样。
只是不需要转换并记录字符串了,递归结束时直接将res+1即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Solution {
int target;
int res;
int[][] operations;
public int totalNQueens(int n) {
target = n;
res = 0;
operations = new int[][]{{-1,0},{-1,1},{-1,-1}};
backtracking(new int[n][n], 0, 0);
return res;
}

private void backtracking(int[][] board, int i, int n){
if(n == target){
res++;
return;
}
for(int j = 0; j < target; j++){
if(isValid(board, i, j)){
board[i][j] = 1;
backtracking(board, i+1, n+1);
board[i][j] = 0;
}
}
}

private boolean isValid(int[][] board, int i, int j){
for(int[] operation : operations){
int p = i + operation[0], q = j + operation[1];
while(p >= 0 && p < target && q >= 0 && q < target){
if(board[p][q] == 1) return false;
p += operation[0];
q += operation[1];
}
}
return true;
}
}
51. N-Queens

51. N-Queens

Question

The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other.

Given an integer n, return all distinct solutions to the n-queens puzzle. You may return the answer in any order.

Each solution contains a distinct board configuration of the n-queens’ placement, where 'Q' and '.' both indicate a queen and an empty space, respectively.

Solution

话说今天出这道题真的不是搞事情嘛?

八皇后问题,回溯+剪枝。按顺序搜索可以减少情况的重复计算。
由于棋盘的大小为n * n,要放n个皇后,因此每一行(列)上都有且只有一位皇后
我们可以依顺序逐行回溯,对逐行的每一列进行回溯。
由于每一行的可选位置都减少1,因此时间复杂度为O(N!)。

回溯

通过一个char数组board[][]记录棋子状态。将board[][]初始化为’.’。
遍历这一行的所有列,如果该位置有效,则board[i][j]改为’Q’,将这个点设置为有棋子,并递归下一行。
回溯,将board[i][j]重新设置为’.’。

当递归次数达到n时,将char[]数组转换为字符串加入答案。

辅助方法 isValid

检测该位置是否有效。即该位置的路径上是否已经有棋子,或该位置已经有棋子。
由于我们的回溯方法是逐行确定棋子位置,因此不需要查看全部八个方向,而是查看之前已经遍历过的行即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class Solution {
List<List<String>> ret;
int size;
int[][] operations;
public List<List<String>> solveNQueens(int n) {
ret = new ArrayList<>();
size = n;
operations = new int[][]{{-1,-1},{-1,1},{-1,0}};
char[][] board = new char[n][n];
for(char[] s : board){
Arrays.fill(s,'.');
}
backtracking(board,0,0);
return ret;
}

private void backtracking(char[][] board, int n, int i){
if(n == size){
List<String> ans = new ArrayList<>();
for(char[] s : board) ans.add(new String(s));
ret.add(ans);
return;
}

for(int j = 0; j < size; j++){
if(isValid(board, i, j)){
board[i][j] = 'Q';
backtracking(board, n+1, i+1);
board[i][j] = '.';
}
}
}

private boolean isValid(char[][] board, int i, int j){
for(int[] operation : operations){
int p = i + operation[0];
int q = j + operation[1];
while(p >= 0 && p < size && q >= 0 && q < size){
if(board[p][q] == 'Q') return false;
p += operation[0];
q += operation[1];
}
}
return true;
}
}

Solution 2

简单回溯。
在回溯时搜索二维位置,因此增加了时间复杂度,正常是过不了的。
通过按顺序进行搜索(只搜索当前行之后的位置)进行剪枝。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class Solution {
List<List<String>> ret;
int size;
int[][] operations;
HashSet<String> set;
public List<List<String>> solveNQueens(int n) {
ret = new ArrayList<>();
size = n;
operations = new int[][]{{1,1},{1,-1},{1,0},{0,1},{0,-1}};
backtracking(new int[n][n],0,0);
return ret;
}

private void backtracking(int[][] border, int n, int start){
if(n == size){
//print(border);
List<String> ans = new ArrayList<>();
for(int i = 0; i < size; i++){
StringBuffer sb = new StringBuffer();
for(int j = 0; j < size; j++){
if(border[i][j] == 1){
sb.append("Q");
}
else{
sb.append(".");
}
}
ans.add(sb.toString());
}
ret.add(ans);
return;
}

for(int i = start; i < size; i++){
for(int j = 0; j < size; j++){
if(border[i][j] == 0){
border[i][j] = 1;
List<int[]> temp = new ArrayList<>();
for(int[] operation : operations){
int p = i, q = j;
while(p >= 0 && p < size && q >= 0 && q < size){
if(border[p][q] == 0){
border[p][q] = 2;
temp.add(new int[]{p,q});
}
p += operation[0];
q += operation[1];
}
}
backtracking(border, n+1, i+1);
for(int[] t : temp){
border[t[0]][t[1]] = 0;
}
border[i][j] = 0;
}
}
}
}
}
304. Range Sum Query 2D - Immutable

304. Range Sum Query 2D - Immutable

Question

Given a 2D matrix matrix, handle multiple queries of the following type:

  • Calculate the sum of the elements of matrix inside the rectangle defined by its upper left corner (row1, col1) and lower right corner (row2, col2).

Implement the NumMatrix class:

  • NumMatrix(int[][] matrix) Initializes the object with the integer matrix matrix.
  • int sumRegion(int row1, int col1, int row2, int col2) Returns the sum of the elements of matrix inside the rectangle defined by its upper left corner (row1, col1) and lower right corner (row2, col2).

Solution

前缀和/动态规划,用一个dp[][]数组记录到matrix[0][0]到matrix[i][j]区域的和。

计算两个点之间形成的总数只需用外侧的位置的前缀和加上内侧位置的前缀和,然后减去两者交换长宽位置的前缀和即可。

递推公式

计算dp[][]数组时,需要将其上侧与左侧所有数字加和,并加上matrix[][]上该位置对应的元素。
用dp[i][j-1] - dp[i-1][j-1]计算出左侧所有数字之和,dp[i-1][j]则是上侧所有数字之和。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class NumMatrix {
int[][] dp;
public NumMatrix(int[][] matrix) {
int m = matrix.length, n = matrix[0].length;
dp = new int[m+1][n+1];

for(int i = 1; i <= matrix.length; i++){
for(int j = 1; j <= matrix[0].length; j++){
dp[i][j] = dp[i][j-1] - dp[i-1][j-1] + dp[i-1][j] + matrix[i-1][j-1];
}
};
}

public int sumRegion(int row1, int col1, int row2, int col2) {
return dp[row1][col1] + dp[row2+1][col2+1] - dp[row1][col2+1] - dp[row2+1][col1];
}
}

/**
* Your NumMatrix object will be instantiated and called as such:
* NumMatrix obj = new NumMatrix(matrix);
* int param_1 = obj.sumRegion(row1,col1,row2,col2);
*/

Solution 2

暴力搜索,直接计算从row1, col1到row2, col2的元素加和。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class NumMatrix {
int[][] mat;
public NumMatrix(int[][] matrix) {
mat = matrix;
}

public int sumRegion(int row1, int col1, int row2, int col2) {
int sum = 0;
for(int i = row1; i <= row2; i++){
for(int j = col1; j <= col2; j++){
sum += mat[i][j];
}
}
return sum;
}
}

/**
* Your NumMatrix object will be instantiated and called as such:
* NumMatrix obj = new NumMatrix(matrix);
* int param_1 = obj.sumRegion(row1,col1,row2,col2);
*/