backtracking 系列 78, 90, 491, 46,

 Backtracking can be solved always as follows:

Pick a starting point.
while(Problem is not solved)
    For each path from the starting point.
        check if selected path is safe, if yes select it
        and make recursive call to rest of the problem
        before which undo the current move.
    End For
If none of the move works out, return false, NO SOLUTON.

 

subset类的题目,不需要关心顺序,同样的元素不同顺序是重复的,因此通常不需要传入visited,需要start起始位置,每次选完从start后面的位置继续选

permutation类的题目,需要关心顺序,不同顺序认为是不同的结果,因此后面元素选完可能前面的元素还需要选,因此需要维护一个visited来判定元素是否已经用过

 

78. Subsets

Given an integer array nums of unique elements, return all possible subsets (the power set).

The solution set must not contain duplicate subsets. Return the solution in any order.

Example 1:

Input: nums = [1,2,3]
Output: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

Example 2:

Input: nums = [0]
Output: [[],[0]]

Constraints:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
  • All the numbers of nums are unique.
index: 0 1 2
value: 1,2,3

start:            []
               /   |   \
0           [1]   [2]  [3]
            / \     \
1       [1,2] [1,3] [2,3]  
          |
2       [1,2,3]

 

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> result = new ArrayList();
        List<Integer> list = new ArrayList();
        //pick up a start point
        helper(nums,0,list,result);
        return result;
    }
    private void helper(int[] nums,int start,List<Integer> list,List<List<Integer>> result){
        //store current senario
        result.add(new ArrayList(list));
        //loop start to end
        for(int i=start;i<nums.length;i++){
            //add current one 
            list.add(nums[i]);
            //go to the deeper recursion
            helper(nums,i+1,list,result);
            //rollback to original senario
            list.remove(list.size()-1);
        }
    }
}

时间复杂度: O(2N)

解法2: masking

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> result = new ArrayList();
        int total = 1<<nums.length;
        for(int mask = 0;mask<total;mask++){
            List<Integer> list = new ArrayList();
            for(int i=0;i<nums.length;i++){
                if( (mask & (1<<i)) != 0 ) list.add(nums[i]);
            }
            result.add(list);
        }
        return result;
    }
}

 解法3:这个通俗易懂

 

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> results = new ArrayList();
        //从第0个元素开始
        dfs(nums,0,results,new ArrayList());
        return results;
    }
    private void dfs(int[] nums,int start,List<List<Integer>> results,List<Integer> list){
        if(start==nums.length){
            results.add(new ArrayList(list));
            return;
        }
        //不添加当前元素
        dfs(nums,start+1,results,list);
        //添加当前元素
        list.add(nums[start]);
        dfs(nums,start+1,results,list);
        list.remove(list.size()-1);
    }
}

 

90. Subsets II

Given an integer array nums that may contain duplicates, return all possible subsets (the power set).

The solution set must not contain duplicate subsets. Return the solution in any order.

Example 1:

Input: nums = [1,2,2]
Output: [[],[1],[1,2],[1,2,2],[2],[2,2]]

Example 2:

Input: nums = [0]
Output: [[],[0]]

Constraints:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10 
class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        //如果有重复要去重的话,必须先排序
        Arrays.sort(nums);
        List<List<Integer>> result = new ArrayList();
        List<Integer> list = new ArrayList();
        helper(nums,0,list,result);
        return result;
    }
    private void helper(int[] nums,int start,List<Integer> list,List<List<Integer>> result){
        result.add(new ArrayList(list));
        for(int i=start;i<nums.length;i++){
            //如果与前一个元素相同且上个元素未选取,那么属于重复场景
            if(i>start && nums[i]==nums[i-1]) continue;
            list.add(nums[i]);
            helper(nums,i+1,list,result);
            list.remove(list.size()-1);
        }
    }
}

时间复杂度: O(2N)

 

class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
        helper(nums, 0, result, new ArrayList(), false);
        return result;
    }
    private void helper(int[] nums, int curr, List<List<Integer>> result, List<Integer> list, boolean lastUsed) {
        if(curr == nums.length) {
            result.add(new ArrayList(list));
            return;
        }
        helper(nums, curr + 1, result, list, false);
        if(curr > 0 && nums[curr] == nums[curr - 1] && !lastUsed) return;
        list.add(nums[curr]);
        helper(nums, curr + 1, result, list, true);
        list.remove(list.size() - 1);
    }
}

 

 

491. Increasing Subsequences
Medium

Given an integer array nums, return all the different possible increasing subsequences of the given array with at least two elements. You may return the answer in any order.

The given array may contain duplicates, and two equal integers should also be considered a special case of increasing sequence.

Example 1:

Input: nums = [4,6,7,7]
Output: [[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]

Example 2:

Input: nums = [4,4,3,2,1]
Output: [[4,4]]

Constraints:

  • 1 <= nums.length <= 15
  • -100 <= nums[i] <= 100

解法:这个题实际还是subset的套娃题。

两个注意点:

1.如果下一个元素不递增,那么我们就不往下走了

2.如何去除重复元素的问题,同一级中如果已经尝试过相同元素,那么我们不需要再试直接跳过

 

 

class Solution {
    public List<List<Integer>> findSubsequences(int[] nums) {
        List<List<Integer>> results = new ArrayList();
        List<Integer> list = new ArrayList();
        subset(nums,0,list,results);
        return results;
    }
    private void subset(int[] nums,int start,List<Integer> list,List<List<Integer>> results){
        if(list.size()>1){
            results.add(new ArrayList(list));
        }
        if(start>=nums.length) return;
        Set<Integer> set = new HashSet();
        for(int i=start;i<nums.length;i++){
            if(set.contains(nums[i])) continue;//这一层中,如果之前选择过相同的元素,那么就是重复场景了
            if(list.isEmpty()||list.get(list.size()-1)<=nums[i]){
                set.add(nums[i]);
                list.add(nums[i]);
                subset(nums,i+1,list,results);
                list.remove(list.size()-1);
            }
        }
    }
}

 

46. Permutations

Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order.

Example 1:

Input: nums = [1,2,3]
Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

Example 2:

Input: nums = [0,1]
Output: [[0,1],[1,0]]

Example 3:

Input: nums = [1]
Output: [[1]]

Constraints:

  • 1 <= nums.length <= 6
  • -10 <= nums[i] <= 10
  • All the integers of nums are unique.
class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> result = new ArrayList();
        boolean[] visited = new boolean[nums.length];
        List<Integer> list = new ArrayList();
        helper(nums,list,result,visited);
        return result;
    }
    private void helper(int[] nums,List<Integer> list,List<List<Integer>> result,boolean[] visited){
        if(list.size()==nums.length) {
            result.add(new ArrayList(list));
            return;
        }
        for(int i=0;i<nums.length;i++){
            if(visited[i]) continue;
            list.add(nums[i]);
            visited[i]=true;
            helper(nums,list,result,visited);
            list.remove(list.size()-1);
            visited[i]=false;
        }
}
}

时间复杂度: N*(N-1)*(N-2)...*2*1 = O(N!) 

47. Permutations II

Given a collection of numbers, nums, that might contain duplicates, return all possible unique permutations in any order.

Example 1:

Input: nums = [1,1,2]
Output:
[[1,1,2],
 [1,2,1],
 [2,1,1]]

Example 2:

Input: nums = [1,2,3]
Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

Constraints:

  • 1 <= nums.length <= 8
  • -10 <= nums[i] <= 10
1,2,3
                                  []
       
pos0        [1]                  [2]                      [3]       

pos1   [1,2] [1,3]         [2,1]  [2,3]              [3,1]    [3,2]

pos2  [1,2,3]  [1,3,2]     [2,1,3]   [2,3,1]       [3,1,2]     [3,2,1]


1,1,3
                                  []
       
pos0        [1]                  [1]                      [3]       

pos1   [1,1] [1,3]         [1,1]  [1,3]              [3,1]    [3,1]

pos2  [1,1,3]  [1,3,1]     [1,1,3]   [1,3,1]       [3,1,1]     [3,1,1]

 

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        //同样的,要去重先排序
        Arrays.sort(nums);
        List<List<Integer>> result = new ArrayList();
        boolean[] visited = new boolean[nums.length];
        List<Integer> list = new ArrayList();
        helper(nums,list,result,visited);
        return result;
    }
    private void helper(int[] nums,List<Integer> list,List<List<Integer>> result,boolean[] visited){
        if(list.size()==nums.length) {
            result.add(new ArrayList(list));
            return;
        }
        for(int i=0;i<nums.length;i++){
            if(visited[i]) continue;
            //如果前一个元素与当前元素相同,而且还没有被访问,属于重复场景
            if(i>0 && nums[i]==nums[i-1] && !visited[i-1]) continue;
            list.add(nums[i]);
            visited[i]=true;
            helper(nums,list,result,visited);
            list.remove(list.size()-1);
            visited[i]=false;
        }
    }
}

时间复杂度:O(N!)

77. Combinations

Given two integers n and k, return all possible combinations of k numbers out of the range [1, n].

You may return the answer in any order.

Example 1:

Input: n = 4, k = 2
Output:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

Example 2:

Input: n = 1, k = 1
Output: [[1]]

Constraints:

  • 1 <= n <= 20
  • 1 <= k <= n

解法:还是subset的套娃题,只不过只要求输出2个元素的subset

class Solution {

    public List<List<Integer>> combine(int n, int k) {
        List<List<Integer>> result = new ArrayList();
        List<Integer> list = new ArrayList();
        helper(n,list,result,1,k);
        return result;
    }
    private void helper(int n,List<Integer> list,List<List<Integer>> result,int start,int k){
        if(list.size()==k) {
            result.add(new ArrayList(list));
            return;
        }
        for(int i=start;i<=n;i++){
            list.add(i);
            helper(n,list,result,i+1,k);
            list.remove(list.size()-1);
        }    
    }
}

 时间复杂度:O(CKN

37. Sudoku Solver
Hard

Write a program to solve a Sudoku puzzle by filling the empty cells.

A sudoku solution must satisfy all of the following rules:

  1. Each of the digits 1-9 must occur exactly once in each row.
  2. Each of the digits 1-9 must occur exactly once in each column.
  3. Each of the digits 1-9 must occur exactly once in each of the 9 3x3 sub-boxes of the grid.

The '.' character indicates empty cells.

Example 1:

Input: board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
Output: [["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
Explanation: The input board is shown above and the only valid solution is shown below:

 

Constraints:

  • board.length == 9
  • board[i].length == 9
  • board[i][j] is a digit or '.'.
  • It is guaranteed that the input board has only one solution.
class Solution {
    public void solveSudoku(char[][] board) {
        fill(board);
    }
    public boolean fill(char[][] board){
        for(int i=0;i<9;i++){
            for(int j=0;j<9;j++){
                if(board[i][j]=='.'){
                    for(int k=0;k<9;k++){
                        char c  = (char)('1'+k);
                        //如果当前valid,继续fill
                        if(valid(board,i,j,c)){
                            board[i][j]=c;
                            if(fill(board)) return true;
                            board[i][j]='.';//如果fill不成功,需要还原
                        }
                    }
                    return false;
                }
            }
        }
        return true;
    }
    public boolean valid(char[][] board,int x,int y,char k){
        //检查行列
        for(int i=0;i<9;i++) {
            if(board[x][i]==k) return false;   
            if(board[i][y]==k) return false;   
        }
        //检查 3*3
        for(int i=(x/3)*3;i<(x/3)*3+3;i++){
            for(int j=(y/3)*3;j<(y/3)*3+3;j++){
                if(board[i][j]==k) return false;
            }
        }
        return true;
    }
}

 

51. N-Queens
Hard

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.

Example 1:

Input: n = 4
Output: [[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above

Example 2:

Input: n = 1
Output: [["Q"]]

Constraints:

  • 1 <= n <= 9
class Solution {
    public List<List<String>> solveNQueens(int n) {
        List<List<String>> result = new ArrayList();
        char[][] board = new char[n][n];
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                board[i][j]='.';
            }
        }
        fill(board,0,result);
        return result;
    }
    public void fill(char[][] board,int k,List<List<String>> result){
        if(k==board.length){
            List<String> list = new ArrayList();
            for(char[] line:board){
                list.add(new String(line));
            }
            result.add(list);
            return;
        }
        //逐列进行填写
        for(int i=0;i<board.length;i++){
            if( board[i][k]=='.' && valid(board,i,k) ){
                board[i][k]='Q';
                fill(board,k+1,result);
                board[i][k]='.';
            }
        }
    }
    private boolean valid( char[][] board,int x,int y ){
        for(int i=0;i<board.length;i++){
            for(int j=0;j<=y;j++){//因为是逐列进行填充,因此之需要检查到当前列即可
                if(board[i][j]=='Q' && (i==x||(x+y)==(i+j)||(x-y)==(i-j))) return false;//1.检查是否有同一行的2.检查是否有在两条对角线的
            }
        }
        return true;
    }
    
}

 

一道面试题

Given a string num that contains only digits and an integer target, return all possibilities to insert the binary operators '+''-', between the digits of num so that the resultant expression evaluates to the target value

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Solution {
    public static void main(String[] args) {
        List<String> list  = getValid("105",5);
        System.out.println(list);

    }
    public static List<String> getValid(String num,int target){
        List<String> result  = new ArrayList<>();
        helper(num,target,0,result,"");
        return result;
    }
    private static void helper(String num,int target,int index,List<String> result,String curr){
        if(target==0 && index==num.length()){
            result.add(curr.substring(1));
            return;
        }
        if(index>=num.length()) return;
        for(int i=index;i<num.length();i++){
            String temp = num.substring(index,i+1);
            int currVal = Integer.parseInt(temp);
            helper(num,target-currVal,i+1,result,curr+"+"+temp);
            if(index>0)
                helper(num,target+currVal,i+1,result,curr+"-"+temp);
        }
    }
}

 时间复杂度: O(N!)  

1723. Find Minimum Time to Finish All Jobs

You are given an integer array jobs, where jobs[i] is the amount of time it takes to complete the ith job.

There are k workers that you can assign jobs to. Each job should be assigned to exactly one worker. The working time of a worker is the sum of the time it takes to complete all jobs assigned to them. Your goal is to devise an optimal assignment such that the maximum working time of any worker is minimized.

Return the minimum possible maximum working time of any assignment.

Example 1:

Input: jobs = [3,2,3], k = 3
Output: 3
Explanation: By assigning each person one job, the maximum time is 3.

Example 2:

Input: jobs = [1,2,4,7,8], k = 2
Output: 11
Explanation: Assign the jobs the following way:
Worker 1: 1, 2, 8 (working time = 1 + 2 + 8 = 11)
Worker 2: 4, 7 (working time = 4 + 7 = 11)
The maximum working time is 11. 

Constraints:

  • 1 <= k <= jobs.length <= 12
  • 1 <= jobs[i] <= 107

纯暴力解法

class Solution {
    int min = Integer.MAX_VALUE;
    public int minimumTimeRequired(int[] jobs, int k) {
        dfs(jobs,jobs.length-1,new int[k]);
        return min;
    }
    public void dfs( int[] jobs, int index,int[] workers ){
        if(index<0){
            int max = 0;
            for(int t:workers) max = Math.max(max,t);
            min = Math.min(min,max);
            return;
        }
        for(int i=0;i<workers.length;i++){
            workers[i]+=jobs[index];
            dfs(jobs,index-1,workers);
            workers[i]-=jobs[index];
        }
    }
}

优化后

class Solution {
    int min = Integer.MAX_VALUE;
    public int minimumTimeRequired(int[] jobs, int k) {
        Arrays.sort(jobs);//这个排序,是为了从最大的工作量开始计算,可以少走一些分支?? 感觉用处不大
        dfs(jobs,jobs.length-1,new int[k]);
        return min;
    }
    public void dfs( int[] jobs, int index, int[] workers ){
        if(index<0){
            min = Math.min(min,max(workers));
            return;
        }
        if(max(workers)>min) return;//如果按照当前安排,最大工作量已经超过之前计算的最小值,那么不需要往下进行了
        for(int i=0;i<workers.length;i++){
            if(i>0 && workers[i]==workers[i-1]) continue;//如果当前worker和前面一个工作量一样,那么无需再算一次
            workers[i]+=jobs[index];
            dfs(jobs,index-1,workers);
            workers[i]-=jobs[index];
        }
    }
    public int max(int[] workers){
        int max = 0;
        for(int t:workers) max = Math.max(max,t);
        return max;
    }
}

今天又做了一遍,感觉对每次求max又有了k时间的优化

class Solution {
    public int minimumTimeRequired(int[] jobs, int k) {
        dfs( jobs,new int[k],0,0 );
        return minTime;
    }
    private int minTime=Integer.MAX_VALUE;
    private void dfs(int[] jobs,int[] buckets,int index,int max){
        //已经完成所有的job
        if( index==jobs.length ){
            minTime = Math.min(minTime,max);
            return;
        }
        //如果安排的结果(最大值)已经大于当前全局结果,直接返回
        if(max>=minTime) return;
        for(int i=0;i<buckets.length;i++){
            //如果前面一个位置是空的,那么在此填充没有意义,相当于重复前面一个位置试过的场景
            if(i>0 && buckets[i-1]==0) continue;
            //将当前job安排到bucket[i]
            buckets[i]+=jobs[index];
            //安排下一个工作
            dfs(jobs,buckets,index+1,Math.max(max,buckets[i]));
            //撤销当前bucket修改,继续下一个尝试
            buckets[i]-=jobs[index];
        }
    }
}

 

 

 

1986. Minimum Number of Work Sessions to Finish the Tasks
Medium

There are n tasks assigned to you. The task times are represented as an integer array tasks of length n, where the ith task takes tasks[i] hours to finish. A work session is when you work for at most sessionTime consecutive hours and then take a break.

You should finish the given tasks in a way that satisfies the following conditions:

  • If you start a task in a work session, you must complete it in the same work session.
  • You can start a new task immediately after finishing the previous one.
  • You may complete the tasks in any order.

Given tasks and sessionTime, return the minimum number of work sessions needed to finish all the tasks following the conditions above.

The tests are generated such that sessionTime is greater than or equal to the maximum element in tasks[i].

Example 1:

Input: tasks = [1,2,3], sessionTime = 3
Output: 2
Explanation: You can finish the tasks in two work sessions.
- First work session: finish the first and the second tasks in 1 + 2 = 3 hours.
- Second work session: finish the third task in 3 hours.

Example 2:

Input: tasks = [3,1,3,1,1], sessionTime = 8
Output: 2
Explanation: You can finish the tasks in two work sessions.
- First work session: finish all the tasks except the last one in 3 + 1 + 3 + 1 = 8 hours.
- Second work session: finish the last task in 1 hour.

Example 3:

Input: tasks = [1,2,3,4,5], sessionTime = 15
Output: 1
Explanation: You can finish all the tasks in one work session. 

Constraints:

  • n == tasks.length
  • 1 <= n <= 14
  • 1 <= tasks[i] <= 10
  • max(tasks[i]) <= sessionTime <= 15
class Solution {
    /*
    思路:逐个遍历每一个task
        1.尝试放入已经创建的session
        2.尝试新创建一个session
      剪枝:如果遇到已经超过当前最有解的可以直接跳过
    */
    private int min = Integer.MAX_VALUE;
    public int minSessions(int[] tasks, int sessionTime) {
        helper(tasks, sessionTime, new ArrayList(), 0);
        return min;
    }
    private void helper(int[] tasks, int sessionTime, List<Integer> sessions, int index) {
        //如果index到结尾说明都放完了
        if(index == tasks.length) {
            min = Math.min(min, sessions.size());
            return;
        }
        //剪枝:如果遇到已经超过当前最有解的可以直接跳过
        if(min <= sessions.size())  return;
        //1.尝试放入已经创建的session
        for(int i = 0; i < sessions.size(); i++) {
            int curr = sessions.get(i);
            if(curr + tasks[index] <= sessionTime) {
                sessions.set(i, curr + tasks[index]);
                helper(tasks, sessionTime, sessions, index + 1);
                sessions.set(i, curr);
            }
        }
        //2.尝试新创建一个session
        sessions.add(tasks[index]);
        helper(tasks, sessionTime, sessions, index + 1);
        sessions.remove(sessions.size() - 1);
    }
}

dp 状态压缩

class Solution {
    public int minSessions(int[] tasks, int sessionTime) {
        // 初始化bit数组 2^n
        int n = tasks.length;
        int[] dp = new int[1<<n];
        Arrays.fill(dp, 100000);

        // 对于逐个state进行遍历,初始化各种组合是否可能在1个session下完成
        // 001,010,011,100,101,110,111
        for(int i = 1; i < (1<<n); i++) {
            int sum = 0;
            for(int k = 0; k < n; k++) {
                if(((i>>k) & 1) == 1) {
                    sum += tasks[k];
                }
            }
            if(sum <= sessionTime) {
                dp[i] = 1;
            }
        }

        // 对于每个state进行计算 001,010,011,100,101,110,111
        for(int state = 1; state < (1<<n); state++) {
            // 对于当前state进行拆解
            /**
            subset - 1 实际上是把 subset 的二进制表示中最右边的 1 变为 0,并把该位右边的所有位变为 1。
            & state 操作保证 subset 中只保留在原始集合 state 中存在的元素。
            因此,这行代码实际上是在生成 subset 的下一个子集,即去除了最右边的元素。
            对于111,将生成如下:
            111 -> 111  000
            111 -> 110  001
            111 -> 101  010
            111 -> 100  011
            111 -> 011  100
            111 -> 010  101
            111 -> 001  110

            110 -> 110  000
            110 -> 100  010
            110 -> 010  100
            */
            for(int subset = state; subset > 0; subset = (subset - 1) & state) {
                dp[state] = Math.min(dp[state], dp[subset] + dp[state - subset]);
            }
        }
        return dp[(1<<n) - 1];
    }
}

 

1434. Number of Ways to Wear Different Hats to Each Other

Hard

There are n people and 40 types of hats labeled from 1 to 40.

Given a list of list of integers hats, where hats[i] is a list of all hats preferred by the i-th person.

Return the number of ways that the n people wear different hats to each other.

Since the answer may be too large, return it modulo 10^9 + 7.

Example 1:

Input: hats = [[3,4],[4,5],[5]]
Output: 1
Explanation: There is only one way to choose hats given the conditions. 
First person choose hat 3, Second person choose hat 4 and last one hat 5.

Example 2:

Input: hats = [[3,5,1],[3,5]]
Output: 4
Explanation: There are 4 ways to choose hats
(3,5), (5,3), (1,3) and (1,5)

Example 3:

Input: hats = [[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4]]
Output: 24
Explanation: Each person can choose hats labeled from 1 to 4.
Number of Permutations of (1,2,3,4) = 24.

Example 4:

Input: hats = [[1,2,3],[2,3,5,6],[1,3,7,9],[1,8,9],[2,5,7]]
Output: 111 

Constraints:

  • n == hats.length
  • 1 <= n <= 10
  • 1 <= hats[i].length <= 40
  • 1 <= hats[i][j] <= 40
  • hats[i] contains a list of unique integers.

这个题用backtracking是可以解的,但是leetcode直接跑的话是会超时的,哎,后续深入学习dp的时候再做优化吧

class Solution {
    int count=0;
    public int numberWays(List<List<Integer>> hats) {
        helper(hats,0,new HashSet<Integer>());
        return count;
    }
    public void helper(List<List<Integer>> hats,int index,Set<Integer> visited){
        if(index==hats.size()){
            count++;
            return;
        } 
        List<Integer> list = hats.get(index);
        for(int i=0;i<list.size();i++){
            int temp = list.get(i);
            if(!visited.contains(temp)){
                visited.add(temp);
                helper(hats,index+1,visited);
                visited.remove(temp);
            }
        }
    }
}

 

1125. Smallest Sufficient Team
Hard

In a project, you have a list of required skills req_skills, and a list of people. The ith person people[i] contains a list of skills that the person has.

Consider a sufficient team: a set of people such that for every required skill in req_skills, there is at least one person in the team who has that skill. We can represent these teams by the index of each person.

  • For example, team = [0, 1, 3] represents the people with skills people[0]people[1], and people[3].

Return any sufficient team of the smallest possible size, represented by the index of each person. You may return the answer in any order.

It is guaranteed an answer exists.

 

Example 1:

Input: req_skills = ["java","nodejs","reactjs"], people = [["java"],["nodejs"],["nodejs","reactjs"]]
Output: [0,2]

Example 2:

Input: req_skills = ["algorithms","math","java","reactjs","csharp","aws"], people = [["algorithms","math","java"],["algorithms","math","reactjs"],["java","csharp","aws"],["reactjs","csharp"],["csharp","math"],["aws","java"]]
Output: [1,2]

 

Constraints:

  • 1 <= req_skills.length <= 16
  • 1 <= req_skills[i].length <= 16
  • req_skills[i] consists of lowercase English letters.
  • All the strings of req_skills are unique.
  • 1 <= people.length <= 60
  • 0 <= people[i].length <= 16
  • 1 <= people[i][j].length <= 16
  • people[i][j] consists of lowercase English letters.
  • All the strings of people[i] are unique.
  • Every skill in people[i] is a skill in req_skills.
  • It is guaranteed a sufficient team exists.
class Solution {
    int min = Integer.MAX_VALUE;
    Set<Integer> result =null;
    public int[] smallestSufficientTeam(String[] req, List<List<String>> people) {
        helper(req,people,new HashSet(),0);
        int[] arr = new int[result.size()];
        int i=0;
        for(int t:result) arr[i++]=t;
        return arr;
    }
    private void helper(String[] req, List<List<String>> people, Set<Integer> set,int index){
        if(index>=req.length){
            if(min>set.size()){
                result = new HashSet(set);
                min = result.size();
            }
            return;
        }
        if(set.size()>=min) return;
        for(int i=0;i<people.size();i++){
            List<String> list = people.get(i);
            if( list.contains(req[index]) ){
                if(!set.contains(i)){
                    set.add(i);
                    helper(req,people,set,index+1);
                    set.remove(i);
                }
                else{
                    helper(req,people,set,index+1);
                }
            }
        }
    }
}

 

437. Path Sum III
Medium

Given the root of a binary tree and an integer targetSum, return the number of paths where the sum of the values along the path equals targetSum.

The path does not need to start or end at the root or a leaf, but it must go downwards (i.e., traveling only from parent nodes to child nodes).

Example 1:

Input: root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
Output: 3
Explanation: The paths that sum to 8 are shown.

Example 2:

Input: root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
Output: 3

Constraints:

  • The number of nodes in the tree is in the range [0, 1000].
  • -109 <= Node.val <= 109
  • -1000 <= targetSum <= 1000

解法: 计算从root开始到后面每个节点的prefixsum,然后每个节点处判定prefixsum满足条件的点

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int pathSum(TreeNode root, int targetSum) {
        List<Integer> list = new ArrayList();
        list.add(0);//在root前的prefixsum为0
        traversal(root,list,targetSum);
        return count;
    }
    int count = 0;
    private void traversal(TreeNode root,List<Integer> prefixSum,int target){
        if(root==null) return ;
        prefixSum.add(prefixSum.get(prefixSum.size()-1)+root.val);//prefixsum
        check(prefixSum,target);
        traversal(root.left,prefixSum,target);
        traversal(root.right,prefixSum,target);
        prefixSum.remove(prefixSum.size()-1);//记得搞完还原现场
    }
    private void check(List<Integer> prefixSum,int target){
        int curr = prefixSum.get(prefixSum.size()-1);
        //只需用当前点与前面的所有prefixsum减一遍,看是否有满足条件的
        for(int i=0;i<prefixSum.size()-1;i++){
            if(curr-prefixSum.get(i)==target) {
                count++;
            }
        }
    }
}

 时间复杂度:O(N*h) n为树节点数, h为树的高度

131. Palindrome Partitioning
Medium

Given a string s, partition s such that every substring of the partition is a palindrome. Return all possible palindrome partitioning of s.

A palindrome string is a string that reads the same backward as forward.

Example 1:

Input: s = "aab"
Output: [["a","a","b"],["aa","b"]]

Example 2:

Input: s = "a"
Output: [["a"]]

Constraints:

  • 1 <= s.length <= 16
  • s contains only lowercase English letters.
class Solution {
    public List<List<String>> partition(String s) {
        List<List<String>> result = new ArrayList();
        partition(s,0,new ArrayList(),result);
        return result;
    }
    private void partition(String s,int start,List<String> list,List<List<String>> result){
        if(start==s.length()) {
            result.add(new ArrayList(list));
            return;
        }
        for(int i=start;i<s.length();i++){
            if(isParlindrom(s,start,i)){
                list.add(s.substring(start,i+1));
                partition(s,i+1,list,result);
                list.remove(list.size()-1);
            }
        }
    }
    private boolean isParlindrom(String s,int left,int right){
        if(left==right) return true;
        while(left<right){
            if(s.charAt(left)!=s.charAt(right)) return false;
            left++;right--;
        }
        return true;
    }
}

 22. Generate Parentheses

Medium

Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

Example 1:

Input: n = 3
Output: ["((()))","(()())","(())()","()(())","()()()"]

Example 2:

Input: n = 1
Output: ["()"] 

Constraints:

  • 1 <= n <= 8
class Solution {
    public List<String> generateParenthesis(int n) {
        List<String> result = new ArrayList();
        dfs(n,n,result,"");
        return result;
    }
    private void dfs(int left,int right,List<String> result,String curr){
        if(left==0&&right==0) result.add(curr);
        if(left>0) //只要(还有剩余,就可以随意放
            dfs(left-1,right,result,curr+"(");
        if(right>0&&right>left) //如果有),需要判定是否已经放了足够多的(
            dfs(left,right-1,result,curr+")");
    }
}

 

301. Remove Invalid Parentheses
Hard

Given a string s that contains parentheses and letters, remove the minimum number of invalid parentheses to make the input string valid.

Return all the possible results. You may return the answer in any order.

Example 1:

Input: s = "()())()"
Output: ["(())()","()()()"]

Example 2:

Input: s = "(a)())()"
Output: ["(a())()","(a)()()"]

Example 3:

Input: s = ")("
Output: [""]

 Constraints:

  • 1 <= s.length <= 25
  • s consists of lowercase English letters and parentheses '(' and ')'.
  • There will be at most 20 parentheses in s.
class Solution {
    public List<String> removeInvalidParentheses(String s) {
        //1.统计left和right至少应该删除的个数
        int left = 0,right = 0;
        for(int i=0;i<s.length();i++){
            char c  = s.charAt(i);
            if(c=='(') left++;
            else if(c==')'){
                if(left==0) right++;
                else left--;
            }
        }
        //2.递归尝试进行删除,直到满足条件加入结果集
        List<String> result = new ArrayList();
        dfs(new StringBuffer(s),left,right,0,result);
        return result;
    }
    private void dfs(StringBuffer s,int left,int right,int currind,List<String> result){
        if(left==0 && right==0) {
            if(isValid(s.toString())) //此时没有多余的括弧了,但是依然要再次检查是否valid(有可能顺序不对)
                result.add(s.toString());
            return;
        }
        for( int i=currind;i<s.length();i++ ){
            StringBuffer temp = new StringBuffer(s.toString());//这个串必须保留下来因为这个是backtracking
            char c = s.charAt(i);
            if(i>currind && s.charAt(i)==s.charAt(i-1)) continue;//去除重复情况
            if(right>0){//如果right>0,证明当前有right括弧在left之前的,因此要先处理这种情况
                if(c==')') dfs(temp.delete(i,i+1),left,right-1,i,result);
            }
            else{
                if(c=='(') dfs(temp.delete(i,i+1),left-1,right,i,result);
            }
        }
    }
    private boolean isValid( String s ){
        int left = 0;
        for(int i=0;i<s.length();i++){
            char c= s.charAt(i);
            if(c=='(') left++;
            else if(c==')'){
                if(left<=0) return false;
                else left--;
            }
        }
        return left==0;
    }
}

 

给定1个全数字的字符串,你要输出把这个string parse之后用+ 或者 - 连起来能结果是100的情况

比如"991"这个例子,你要输出 ["9+91", "99+1"] 

public class Solution3 {
    public static void main(String[] args) {
        List<List<Integer>> result = new ArrayList<>();
        dfs("991",0,0,100,result,new ArrayList<>());
        System.out.println(result);
    }
    static void dfs(String s, int start , int val, int target, List<List<Integer>> result, List<Integer> list){
        if(start==s.length()){
            if(val==target) result.add(new ArrayList<>(list));
        }
        for(int i=start;i<s.length();i++){
            int curr = Integer.parseInt(s.substring(start,i+1));
            list.add(curr);
            dfs(s,i+1,curr+val,target,result,list);
            list.remove(list.size()-1);
        }
    }
}

 332. Reconstruct Itinerary

Hard

You are given a list of airline tickets where tickets[i] = [fromi, toi] represent the departure and the arrival airports of one flight. Reconstruct the itinerary in order and return it.

All of the tickets belong to a man who departs from "JFK", thus, the itinerary must begin with "JFK". If there are multiple valid itineraries, you should return the itinerary that has the smallest lexical order when read as a single string.

  • For example, the itinerary ["JFK", "LGA"] has a smaller lexical order than ["JFK", "LGB"].

You may assume all tickets form at least one valid itinerary. You must use all the tickets once and only once.

Example 1:

Input: tickets = [["MUC","LHR"],["JFK","MUC"],["SFO","SJC"],["LHR","SFO"]]
Output: ["JFK","MUC","LHR","SFO","SJC"]

Example 2:

Input: tickets = [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]
Output: ["JFK","ATL","JFK","SFO","ATL","SFO"]
Explanation: Another possible reconstruction is ["JFK","SFO","ATL","JFK","ATL","SFO"] but it is larger in lexical order. 

Constraints:

  • 1 <= tickets.length <= 300
  • tickets[i].length == 2
  • fromi.length == 3
  • toi.length == 3
  • fromi and toi consist of uppercase English letters.
  • fromi != toi
class Solution {
    public List<String> findItinerary(List<List<String>> tickets) {
        //1.build graph
        Map<String,List<String>> graph = new HashMap();
        for(List<String> pair:tickets){
            String from = pair.get(0);
            String to = pair.get(1);
            List<String> list = graph.getOrDefault(from,new ArrayList());
            graph.put(from,list);
            list.add(to);
        }
        for(String key:graph.keySet()) Collections.sort(graph.get(key));
        
        //2.dfs
        List<String> result = new ArrayList();
        result.add("JFK");
        travel(graph,"JFK",result,tickets.size());
        return result;
    }
    private boolean travel(Map<String,List<String>> graph,String from,List<String> result,int count){
        if(count==0) return true;
        List<String> list = graph.getOrDefault(from,Arrays.asList());
        if(list.isEmpty()) return false;
        List<String> temp = new ArrayList(list);
        for(String to:temp){
            list.remove(to);
            result.add(to);
            if(travel(graph,to,result,count-1)) return true;
            list.add(to);
            result.remove(result.size()-1);
        }
        return false;
    }
}

 

class Solution {
    public List<String> findItinerary(List<List<String>> tickets) {
        //1.build map
        Map<String,PriorityQueue<String>> map = new HashMap();
        for(List<String> pair:tickets){
            PriorityQueue<String> list = map.getOrDefault(pair.get(0),new PriorityQueue());
            map.put(pair.get(0),list);
            list.offer(pair.get(1));
        }

        //2.dfs to find the best path
        List<String> result  = new ArrayList();
        findPath(map,"JFK",result);
        Collections.reverse(result);
        return result;
    }
    public void findPath( Map<String,PriorityQueue<String>> map, String start,List<String> result ){
        PriorityQueue<String> pq = map.get(start);
        if(pq!=null){
            while(!pq.isEmpty()){
                String dest = pq.poll();
                findPath(map,dest,result);
            }
        }
        result.add(start);
    }
}

 

291. Word Pattern II
Medium

Given a pattern and a string s, return true if s matches the pattern.

A string s matches a pattern if there is some bijective mapping of single characters to strings such that if each character in pattern is replaced by the string it maps to, then the resulting string is s. A bijective mapping means that no two characters map to the same string, and no character maps to two different strings.

 Example 1:

Input: pattern = "abab", s = "redblueredblue"
Output: true
Explanation: One possible mapping is as follows:
'a' -> "red"
'b' -> "blue"

Example 2:

Input: pattern = "aaaa", s = "asdasdasdasd"
Output: true
Explanation: One possible mapping is as follows:
'a' -> "asd"

Example 3:

Input: pattern = "aabb", s = "xyzabcxzyabc"
Output: false 

Constraints:

  • 1 <= pattern.length, s.length <= 20
  • pattern and s consist of only lowercase English letters.
class Solution {
    public boolean wordPatternMatch(String pattern, String s) {
        Map<Character,String> map1 = new HashMap(); //用于存放 pattern-子串 的关系
        Set<String> set = new HashSet();//用于存放子串,用于检查子串是否已经被其他pattern使用
        return dfs( pattern, s, 0, 0, map1, set );
    }
    private boolean dfs( String pattern, String s, int i, int j, Map<Character,String> map1, Set<String> set ){
        //如果pattern和s都到头了,返回true
        if( i==pattern.length() && j==s.length() ) return true;
        //如果其中一个到头了,肯定不符合,返回false
        if( i==pattern.length() || j==s.length() ) return false;
        //取出当前pattern
        char c = pattern.charAt(i);
        //如果已经有这个pattern
        if(map1.containsKey(c)){
            //从pattern中取出对一个的子串
            String temp = map1.get(c);
            //看子串是否与s中对应长度子串一致,一致的话继续后面的匹配
            if( j+temp.length() <= s.length() && temp.equals( s.substring(j,j+temp.length()) ) ) 
                return dfs(pattern,s,i+1,j+temp.length(),map1,set);
            //否则返回false
            else
                return false;
        }
        //如果这个pattern第一次出现
        else{
            //尝试剩余的所有子串,从1个到个
            for( int k=j+1; k<=s.length(); k++ ){
                String temp = s.substring(j,k);
                //如果这个子串已经被其他pattern用过,那么不能使用
                if(set.contains(temp)) continue;
                //将该pattern-子串 尝试加入
                map1.put(c,temp);
                set.add(temp);
                //继续后面的匹配
                if(dfs(pattern,s,i+1,k,map1,set)) return true;
                //如果后续匹配不成功,删除刚加入的尝试 pattern-子串,  继续下一个子串尝试
                map1.remove(c);
                set.remove(temp);
            }
            return false;
        }
    }
}

 140. Word Break II

Hard

Given a string s and a dictionary of strings wordDict, add spaces in s to construct a sentence where each word is a valid dictionary word. Return all such possible sentences in any order.

Note that the same word in the dictionary may be reused multiple times in the segmentation.

 Example 1:

Input: s = "catsanddog", wordDict = ["cat","cats","and","sand","dog"]
Output: ["cats and dog","cat sand dog"]

Example 2:

Input: s = "pineapplepenapple", wordDict = ["apple","pen","applepen","pine","pineapple"]
Output: ["pine apple pen apple","pineapple pen apple","pine applepen apple"]
Explanation: Note that you are allowed to reuse a dictionary word.

Example 3:

Input: s = "catsandog", wordDict = ["cats","dog","sand","and","cat"]
Output: []

Constraints:

  • 1 <= s.length <= 20
  • 1 <= wordDict.length <= 1000
  • 1 <= wordDict[i].length <= 10
  • s and wordDict[i] consist of only lowercase English letters.
  • All the strings of wordDict are unique.

解法1:

class Solution {
    public List<String> wordBreak(String s, List<String> wordDict) {
        Set<String> set = new HashSet();
        for( String str: wordDict ) set.add(str);
        List<String> result = new ArrayList();
        dfs(s,set,result,0,"");
        return result;
    }
    private void dfs( String s, Set<String> set, List<String> result, int start, String curr ){
        if( start == s.length() ) {
            result.add(curr);
            return;
        }
        for(int i=start+1;i<=s.length();i++){
            String temp = s.substring(start,i);
            if( set.contains( temp ) ) {
                if(start==0)//这个地方只是表示开头的第一个单词不需要加空格分割
                    dfs( s,set,result,i,temp );
                else
                    dfs( s,set,result,i,curr+" "+temp );
            }
        }
    }
}

解法2:

class Solution {
    public List<String> wordBreak(String s, List<String> wordDict) {
        Set<String> set  = new HashSet();
        for(String str:wordDict) set.add(str);
        Map<Integer,List<String>> map = new HashMap();
        return helper(s,0,map,set);
    }
    public List<String> helper(String s,int start,Map<Integer,List<String>> map,Set<String> set){
        if(map.get(start)!=null) return map.get(start);
        List<String> list = new LinkedList();
        for(int i=start;i<s.length();i++){
            if( set.contains(s.substring(start,i+1))){
                if(i==s.length()-1) list.add(s.substring(start,i+1));
                else{
                    List<String> temp = helper(s,i+1,map,set);
                    for(String str:temp){
                        list.add(s.substring(start,i+1)+" "+str);
                    }                    
                }
            }
        }
        map.put(start,list);
        return list;
    }
}

 

2049. Count Nodes With the Highest Score
Medium

There is a binary tree rooted at 0 consisting of n nodes. The nodes are labeled from 0 to n - 1. You are given a 0-indexed integer array parents representing the tree, where parents[i] is the parent of node i. Since node 0 is the root, parents[0] == -1.

Each node has a score. To find the score of a node, consider if the node and the edges connected to it were removed. The tree would become one or more non-empty subtrees. The size of a subtree is the number of the nodes in it. The score of the node is the product of the sizes of all those subtrees.

Return the number of nodes that have the highest score.

 

Example 1:

example-1
Input: parents = [-1,2,0,2,0]
Output: 3
Explanation:
- The score of node 0 is: 3 * 1 = 3
- The score of node 1 is: 4 = 4
- The score of node 2 is: 1 * 1 * 2 = 2
- The score of node 3 is: 4 = 4
- The score of node 4 is: 4 = 4
The highest score is 4, and three nodes (node 1, node 3, and node 4) have the highest score.

Example 2:

example-2
Input: parents = [-1,2,0]
Output: 2
Explanation:
- The score of node 0 is: 2 = 2
- The score of node 1 is: 2 = 2
- The score of node 2 is: 1 * 1 = 1
The highest score is 2, and two nodes (node 0 and node 1) have the highest score.

Constraints:

  • n == parents.length
  • 2 <= n <= 105
  • parents[0] == -1
  • 0 <= parents[i] <= n - 1 for i != 0
  • parents represents a valid binary tree.
class Solution {
    public int countHighestScoreNodes(int[] parents) {
        //建树
        Map<Integer,List<Integer>> map = new HashMap<>();
        for(int i=0;i<parents.length;i++){
            List<Integer> list = map.getOrDefault(parents[i],new ArrayList());
            map.put(parents[i],list);
            list.add(i);
        }
        //dfs自下而上而上得到结果
        long[] results = new long[parents.length];
        dfs( map, 0, parents.length, results, parents  );
        
        //得到最大值
        long max = Arrays.stream( results ).max().getAsLong();
        int count = 0;
        //计算最大值个数,并返回
        for(long num:results) if(max==num) count++;
        return count;
    }
    private int dfs( Map<Integer, List<Integer>> map, int root, int len, long[] results ,int[] parents){
        List<Integer> list = map.get( root );
        //叶子节点直接返回
        if(list==null || list.isEmpty()) {
            results[root]=len-1;
            return 1;
        }
        long product = 1;
        int sum = 0;
        //遍历子节点,获得总量和总乘积
        for(int i=0;i<list.size();i++){
            int count = dfs( map, list.get(i), len, results, parents );
            product *= count;
            sum += count;
        }
        //如果是根节点,那么子节点的乘积就是它的结果
        if(parents[root]==-1) results[root] = product;
        //如果不是根节点,那么就是 当前节点子节点的乘积*此子树除外的节点数
        else results[root] = product*(len-sum-1);
        //返回当前子树的节点个数: 子节点+当前节点
        return sum+1;
    }
}

 2375. Construct Smallest Number From DI String

Medium

You are given a 0-indexed string pattern of length n consisting of the characters 'I' meaning increasing and 'D' meaning decreasing.

A 0-indexed string num of length n + 1 is created using the following conditions:

  • num consists of the digits '1' to '9', where each digit is used at most once.
  • If pattern[i] == 'I', then num[i] < num[i + 1].
  • If pattern[i] == 'D', then num[i] > num[i + 1].

Return the lexicographically smallest possible string num that meets the conditions.

 Example 1:

Input: pattern = "IIIDIDDD"
Output: "123549876"
Explanation:
At indices 0, 1, 2, and 4 we must have that num[i] < num[i+1].
At indices 3, 5, 6, and 7 we must have that num[i] > num[i+1].
Some possible values of num are "245639871", "135749862", and "123849765".
It can be proven that "123549876" is the smallest possible num that meets the conditions.
Note that "123414321" is not possible because the digit '1' is used more than once.

Example 2:

Input: pattern = "DDD"
Output: "4321"
Explanation:
Some possible values of num are "9876", "7321", and "8742".
It can be proven that "4321" is the smallest possible num that meets the conditions.

 Constraints:

  • 1 <= pattern.length <= 8
  • pattern consists of only the letters 'I' and 'D'.
class Solution {
    String finalResult = "";
    public String smallestNumber(String pattern) {
        boolean[] visited = new boolean[10];
        //从1到9,数字逐个尝试
        for(int i=1;i<=9;i++){
            visited[i] = true;
            //从第0个位置开始尝试
            if(dfs(pattern, 0, visited, ""+i, i)) return finalResult;
            visited[i] = false;
        }
        return finalResult;
    }
    private boolean dfs(String pattern, int index, boolean[] visited, String result, int pre){
        //如果已经匹配完了,保留结果,返回true
        if(index>=pattern.length()) {
            finalResult = result;
            return true;
        }
        //如果是I,那么只能从比上一个数字大的数开始尝试  pre+1 ~ 9
        if(pattern.charAt(index) == 'I'){
            for(int i=pre+1; i<=9; i++){
                if(visited[i]) continue;
                visited[i] = true;
                if(dfs(pattern, index+1, visited, result+i, i)) return true;
                visited[i] = false;
            }
        }
        //如果是D,那么只能尝试小于上一个数的情况    1 ~ pre-1
        else{
            for(int i=1;i<=pre-1;i++){
                if(visited[i]) continue;
                visited[i] = true;
                if(dfs(pattern, index+1, visited, result+i, i)) return true;
                visited[i] = false;
            }
        }
        return false;
    }
}

 1986. Minimum Number of Work Sessions to Finish the Tasks

Medium

There are n tasks assigned to you. The task times are represented as an integer array tasks of length n, where the ith task takes tasks[i] hours to finish. A work session is when you work for at most sessionTime consecutive hours and then take a break.

You should finish the given tasks in a way that satisfies the following conditions:

  • If you start a task in a work session, you must complete it in the same work session.
  • You can start a new task immediately after finishing the previous one.
  • You may complete the tasks in any order.

Given tasks and sessionTime, return the minimum number of work sessions needed to finish all the tasks following the conditions above.

The tests are generated such that sessionTime is greater than or equal to the maximum element in tasks[i].

 Example 1:

Input: tasks = [1,2,3], sessionTime = 3
Output: 2
Explanation: You can finish the tasks in two work sessions.
- First work session: finish the first and the second tasks in 1 + 2 = 3 hours.
- Second work session: finish the third task in 3 hours.

Example 2:

Input: tasks = [3,1,3,1,1], sessionTime = 8
Output: 2
Explanation: You can finish the tasks in two work sessions.
- First work session: finish all the tasks except the last one in 3 + 1 + 3 + 1 = 8 hours.
- Second work session: finish the last task in 1 hour.

Example 3:

Input: tasks = [1,2,3,4,5], sessionTime = 15
Output: 1
Explanation: You can finish all the tasks in one work session.

 Constraints:

  • n == tasks.length
  • 1 <= n <= 14
  • 1 <= tasks[i] <= 10
  • max(tasks[i]) <= sessionTime <= 15
class Solution {
    /*
    思路:逐个遍历每一个task
        1.尝试放入已经创建的session
        2.尝试新创建一个session
      剪枝:如果遇到已经超过当前最有解的可以直接跳过
    */
    private int min = Integer.MAX_VALUE;
    public int minSessions(int[] tasks, int sessionTime) {
        helper(tasks, sessionTime, new ArrayList(), 0);
        return min;
    }
    private void helper(int[] tasks, int sessionTime, List<Integer> sessions, int index) {
        //如果index到结尾说明都放完了
        if(index == tasks.length) {
            min = Math.min(min, sessions.size());
            return;
        }
        //剪枝:如果遇到已经超过当前最有解的可以直接跳过
        if(min <= sessions.size())  return;
        //1.尝试放入已经创建的session
        for(int i = 0; i < sessions.size(); i++) {
            int curr = sessions.get(i);
            if(curr + tasks[index] <= sessionTime) {
                sessions.set(i, curr + tasks[index]);
                helper(tasks, sessionTime, sessions, index + 1);
                sessions.set(i, curr);
            }
        }
        //2.尝试新创建一个session
        sessions.add(tasks[index]);
        helper(tasks, sessionTime, sessions, index + 1);
        sessions.remove(sessions.size() - 1);
    }
}

 

You are given a 0-indexed 2D integer matrix grid of size 3 * 3, representing the number of stones in each cell. The grid contains exactly 9 stones, and there can be multiple stones in a single cell.

In one move, you can move a single stone from its current cell to any other cell if the two cells share a side.

Return the minimum number of moves required to place one stone in each cell.

 

Example 1:

Input: grid = [[1,1,0],[1,1,1],[1,2,1]]
Output: 3
Explanation: One possible sequence of moves to place one stone in each cell is: 
1- Move one stone from cell (2,1) to cell (2,2).
2- Move one stone from cell (2,2) to cell (1,2).
3- Move one stone from cell (1,2) to cell (0,2).
In total, it takes 3 moves to place one stone in each cell of the grid.
It can be shown that 3 is the minimum number of moves required to place one stone in each cell.

Example 2:

Input: grid = [[1,3,0],[1,0,0],[1,0,3]]
Output: 4
Explanation: One possible sequence of moves to place one stone in each cell is:
1- Move one stone from cell (0,1) to cell (0,2).
2- Move one stone from cell (0,1) to cell (1,1).
3- Move one stone from cell (2,2) to cell (1,2).
4- Move one stone from cell (2,2) to cell (2,1).
In total, it takes 4 moves to place one stone in each cell of the grid.
It can be shown that 4 is the minimum number of moves required to place one stone in each cell.

 

Constraints:

  • grid.length == grid[i].length == 3
  • 0 <= grid[i][j] <= 9
  • Sum of grid is equal to 9.
class Solution {
    /**
    可以认为尝试将大于1的格子,填入为0的格子的,需要寻找最优的填法
     */
    int result = Integer.MAX_VALUE;
    public int minimumMoves(int[][] grid) {
        dfs(0, 0, grid);
        return result;
    }
    /**
        curr: 0~8
        step: 截至当前使用的步数
        grid: 格子
     */
    private void dfs(int curr, int step, int[][] grid) {
        // 已经填完了
        if(curr == 9) {
            result = Math.min(result, step);
            return;
        }
        // 如果填到当前已经超过了最优值,那么可以剪枝
        if(step >= result) return;
        // 将flag index 转为坐标
        int x = curr / 3;
        int y = curr % 3;
        // 如果为0的话才需要找填充值
        if(grid[x][y] == 0) {
            for(int i = 0; i < 3; i++) {
                for(int j = 0; j < 3; j++) {
                    if(grid[i][j] <= 1) continue;
                    grid[x][y] += 1;
                    grid[i][j] -= 1;
                    dfs(curr + 1, step + Math.abs(x - i) + Math.abs(y - j), grid);
                    grid[x][y] -= 1;
                    grid[i][j] += 1;
                }
            }
        }
        // 不为0的话直接过
        else{
            dfs(curr + 1, step, grid);
        }

    }
}

 

posted @ 2021-10-18 00:01  xiaoyongyong  阅读(132)  评论(0)    收藏  举报