回溯(backtracking)

回溯(抽象成树型结构、一般无返回值backtracking)

回溯算法大纲

1. 理论基础

回溯 和递归相辅相成

  • 一般递归函数下面的部分就是回溯的逻辑
  • 默认是纯暴力(后续可以剪枝)

应用:

  • 组合【没有顺序】
  • 切割
  • 子集
  • 排列【有顺序】
  • 棋盘
    • N 皇后
    • 解数独

回溯法都可以抽象为一个树型结构

  • 树的宽度:集合大小
  • 树的深度:递归深度

回溯模板

void backtracking(参数){
	if(终止条件){
		收集结果	# 通常在叶子节点
		return;
	}
	for(集合元素集){
		处理节点;
		backracking(路径、选择列表);	# 递归
		回溯,撤销处理结果
	}
}

回溯三部曲:

  1. 递归函数参数、返回值
  2. 确定终止条件
  3. 单层递归逻辑

77. 组合(没有顺序)

注意:这里 k:递归的层数

ccdd

class Solution {
	List<Integer> temp =  new ArrayList<>();
	List<List<Integer>> res = new ArrayList<>();

    public List<List<Integer>> combine(int n, int k) {
		backtracking(1, n, k);
		return res;
    }

    public void backtracking(int start, int n, int k){
    	if (k == 0){	//	到叶子节点了
    		res.add(new ArrayList<>(temp));
			return;
		}
		//	注意:如果是取出第一个节点的话:i 的极限是:n - k + 1
        //	随着 temp 增大,i 的极限也会向右推进
        
		for (int i = start; i <= n - k + 1; i++) {	// i:1 -> 3【n - k + 1】
			temp.add(i);
			backtracking(i + 1, n, k - 1);
			temp.remove(temp.size() - 1);	//	回溯
		}
	}
}

组合问题剪枝

如果早就直到达不成要求的话,就提前终止

image-20230825115653144

image-20230825123405401

216. 组合总和Ⅲ【在一个集合中求解(利用 start 避免得到重复的组合)】

注意:⭐

我们每次都要存储list 内的临时值

否则,最后

  • 由于 list 最后一定是 null
  • 而 list 是引用类型
  • 所以res 中存放的也只是 null

验证下:

输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
解释:
1 + 2 + 6 = 9
1 + 3 + 5 = 9
2 + 3 + 4 = 9
没有其他符合的组合了。

# 如果猜的没错的话,res 中有三个 null

image-20230825160207794

// 理解:不能直接写 res.add(list)
//	1. list 是引用类型
//	2. 由于对于list 的操作,每次都在添加后,马上清除
//	   所以如果仅仅存放 list的话只有 null
res.add(new ArrayList<>(list));

再次验证:

image-20230825160621976

class Solution {
	List<List<Integer>> res = new ArrayList<>();
	List<Integer> list = new ArrayList<>();
	//	相加和为n 的 k 个数
	public List<List<Integer>> combinationSum3(int k, int n) {
		int sum = 0;
		for (int i = 1; i <= 9; i++) {
			sum += i;
		}
		if (n > sum){
			return res;
		}
		backtracking(1, n, k);
		return res;
	}
	public void backtracking(int start, int n, int k){
		if (n == 0 && k == 0){
			res.add((new ArrayList<>(list)));
			return;
		}
		if (k == 0){
			return;
		}

		//	如果当前值 > n
		for (int i = start; i <= 9 - k + 1; i++) {	//	确定起始位置
			list.add(i);
			backtracking(i + 1, n - i, k - 1);
			list.remove(list.size() - 1);
		}
	}
}

17. 电话号码的字母组合【2个集合,无需 start】

class Solution {
	List<String> list = new ArrayList<>();
	Map<Integer, String> map = new HashMap<>();
	StringBuilder sb = new StringBuilder();
	public List<String> letterCombinations(String digits) {
		if (digits.length() == 0){
			return list;
		}
		map.put(2, "abc");
		map.put(3, "def");
		map.put(4, "ghi");
		map.put(5, "jkl");
		map.put(6, "mno");
		map.put(7, "pqrs");
		map.put(8, "tuv");
		map.put(9, "wxyz");
		backtracking(digits, 0);
		return list;
	}

	public void backtracking(String str, int index){
		if (index == str.length()){	//	指向末尾才真正结束
			list.add(sb.toString());
			return;
		}

		int temp = Integer.parseInt(str.charAt(index)+"");
		String s = map.get(temp);	//	第一个数字对应的字符串
		for (int j = 0; j < s.length(); j++) {
			sb.append(s.charAt(j));
			backtracking(str, index + 1);
			sb.delete(sb.length() - 1, sb.length());
		}
	}
}

39. 组合总和

可以重复选取

class Solution {
	List<Integer> list = new ArrayList<>();
	List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
		backtrack(candidates, 0, target);
		return res;
	}

    public void backtrack(int[] arr, int start, int target){
    	if (target < 0){
    		return;
		}

    	if (target == 0){
			res.add(new ArrayList<>(list));
    		return;
		}
		for (int i = start; i < arr.length; i++) {
			list.add(arr[i]);
			backtrack(arr, i,target - arr[i]);	//	由于元素可以重复取,所以 这里 i 不加一了就
			list.remove(list.size() - 1);
		}
	}
}

40. 组合总和Ⅱ【组合去重,先排序,如果和上一个相同就右移】

如果用 Set 的话,底层是红黑树,效率较低!!!

class Solution {
	List<Integer> list = new ArrayList<>();
	List<List<Integer>> res = new ArrayList<>();

    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
    	Arrays.sort(candidates);
		backtracking(candidates, 0, target);
		return res;
	}

	public void backtracking(int[] arr, int start, int target){
    	if (target < 0){
    		return;
		}
    	if (target == 0){
    		res.add(new ArrayList<>(list));
			return;
		}

		for (int i = start; i < arr.length; i++) {
			list.add(arr[i]);
			backtracking(arr, i + 1, target - arr[i]);
			list.remove(list.size() - 1);
			while (i < arr.length - 1 && arr[i + 1] == arr[i]){	//	如果下一步和当初选择的一致,那么跳过即可!!!
				i++;
			}
		}
	}
}

131. 分割回文串

对应的树型结构【和组合类似】

class Solution {
	List<String> list = new ArrayList<>();
	List<List<String>> res = new ArrayList<>();

    public List<List<String>> partition(String s) {
		backtracking(s, 0);
		return res;
	}

	public void backtracking(String str, int index){
    	if (index == str.length()){
    		res.add(new ArrayList<>(list));
    		return;
		}
		for (int i = index; i < str.length(); i++) {	//	注意 index 是定制
			String substring = str.substring(index, i + 1);
			if (isReverse(substring)) {
				list.add(str.substring(index, i + 1));
			}else{
				continue;
			}
			backtracking(str, i + 1);	//	在 str 的 i + 1 位置处继续递归
			list.remove(list.size() - 1);
		}
	}

	public boolean isReverse(String str){
    	int l = 0;
    	int r = str.length() - 1;
    	while (l < r){
    		if (str.charAt(l) != str.charAt(r)){
    			return false;
			}
    		l++;
    		r--;
		}
		return true;
	}
}

93. 复原 IP 地址

class Solution {
	StringBuilder sb = new StringBuilder();
	List<String> list = new ArrayList<>();
    public List<String> restoreIpAddresses(String s) {
		backtracking(s, 0, 4);
		System.out.println(list);
		return list;
	}

    public void backtracking(String str, int startIndex, int k){
    	if (startIndex == str.length() && k == 0){
    		list.add(sb.substring(0, sb.length() - 1));	//	取出末尾 .
    		return;
		}

		for (int i = startIndex; i < str.length(); i++) {
			String substring = str.substring(startIndex, i + 1);
			int len = substring.length();	//	添加字符串的长度
			if (isValid(substring)){
				sb.append(substring).append(".");
			}else {
				continue;
			}
			backtracking(str, i + 1, k - 1);
			sb.delete(sb.length() - len - 1, sb.length());
		}
	}

	public boolean isValid(String str){
		char[] chars = str.toCharArray();
		for (char aChar : chars) {
			if (!Character.isDigit(aChar)){
				return false;
			}
		}

		if (str.length() > 3) {
			return false;
		}
		int i = Integer.parseInt(str);
    	if (i < 0 || i > 255){
    		return false;
		}
		return (i + "").equals(str);	//	不含前导 0
	}
}

78. 子集【收集全部结果】

注意:这里不能提前 return

因为第一次 list 为 [],如果 return了,后面的代码不会运行

class Solution {
	List<Integer> list = new ArrayList<>();
	List<List<Integer>> res = new ArrayList<>();

    public List<List<Integer>> subsets(int[] nums) {
		backtracking(nums, 0);
		return res;
	}

	public void backtracking(int[] arr, int startIndex){
    	if (startIndex <= arr.length){
    		res.add(new ArrayList<>(list));	//	第一次 就收集到了 []
		}
		for (int i = startIndex; i < arr.length; i++) {
			list.add(arr[i]);
			backtracking(arr, i + 1);
			list.remove(list.size() - 1);
		}
	}
}

image-20230828111429674

90. 子集Ⅱ【先排序,后去重】

class Solution {
	List<Integer> list = new ArrayList<>();
	List<List<Integer>> res = new ArrayList<>();

    public List<List<Integer>> subsetsWithDup(int[] nums) {
		Arrays.sort(nums);
    	backtracking(nums, 0);
    	return res;
    }
	public void backtracking(int[] arr, int startIndex){
		if (startIndex <= arr.length){
			res.add(new ArrayList<>(list));	//	第一次 就收集到了 []
		}
		for (int i = startIndex; i < arr.length; i++) {
			list.add(arr[i]);
			backtracking(arr, i + 1);
			list.remove(list.size() - 1);
			while (i < arr.length - 1 && arr[i + 1] == arr[i]){
				i++;
			}
		}
	}
}

491. 递增子序列

image-20230828153559054

但是同一树枝上是可以重复取的,其实也没有重复取,它取的是不同的元素,仅仅是不同的元素数值相同而已

前面的 7 下的所有分支,一定包含了后面的 7 的所有分支

class Solution {
	List<Integer> list = new ArrayList<>();
	List<List<Integer>> res = new ArrayList<>();

	public List<List<Integer>> findSubsequences(int[] nums) {
		backtracking(nums, 0);
		return res;
    }

	public void backtracking(int[] arr, int startIndex){
		if (startIndex <= arr.length && list.size() >= 2){
			res.add(new ArrayList<>(list));	//	第一次 就收集到了 []
		}
		Set<Integer> set = new HashSet<>();	//	每一层都用 set 去重
		for (int i = startIndex; i < arr.length; i++) {
			if (isMore(arr[i]) && set.add(arr[i])){
				list.add(arr[i]);
			}else {
				continue;
			}
			backtracking(arr, i + 1);
			list.remove(list.size() - 1);
		}
	}

	public boolean isMore(int num){
    	if (list.size() == 0){
    		return true;
		}
		Integer integer = list.get(list.size() - 1);
    	return (num - integer) >= 0;
	}
}

46. 全排列【强调元素顺序】

看下排列和组合的区别???

image-20230828193556279

class Solution {
	List<Integer> list = new ArrayList<>();
	List<List<Integer>> res = new ArrayList<>();
	int[] used;

	public List<List<Integer>> permute(int[] nums) {
		used = new int[nums.length];
		backtracking(nums);
		return res;
    }

    public void backtracking(int[] arr){
		if (list.size() == arr.length){
    		res.add(new ArrayList<>(list));
			return;
		}
		for (int i = 0; i < arr.length; i++) {
			if (used[i] != 1) {
				list.add(arr[i]);
				used[i] += 1;
			}else {
				continue;
			}
			backtracking(arr);
			list.remove(list.size() - 1);
			used[i] = 0; //	回溯
		}
	}
}

47. 全排列Ⅱ(有重复的)

class Solution {
	List<Integer> list = new ArrayList<>();
	List<List<Integer>> res = new ArrayList<>();
	int[] used;

	public List<List<Integer>> permuteUnique(int[] nums) {
		used = new int[nums.length];
		Arrays.sort(nums);	//	先排序
		backtracking(nums);
		return res;
    }

	public void backtracking(int[] arr){
		if (list.size() == arr.length){
			res.add(new ArrayList<>(list));
			return;
		}
		for (int i = 0; i < arr.length; i++) {
			if (used[i] != 1) {
				list.add(arr[i]);
				used[i] += 1;
			}else {
				continue;
			}
			backtracking(arr);
			list.remove(list.size() - 1);
			used[i] = 0; //	回溯
			while (i < arr.length - 1 && arr[i + 1] == arr[i]){
				i++;
			}
		}
	}
}

332. 重新安排行程

  1. 先排序
  2. 那么找到的第一个就是符合条件的,就是最小的
    class Solution {
        boolean[] used;
        List<ticket> list = new ArrayList<>();
        List<List<ticket>> res = new ArrayList<>();
        int N;
        ticket[] arr;

        public List<String> findItinerary(List<List<String>> tickets) {
            N = tickets.size();
            used = new boolean[N];
            arr = new ticket[N];
            tickets.sort(new Comparator<List<String>>() {
                @Override
                public int compare(List<String> o1, List<String> o2) {
                    for (int i = 0; i < o1.size(); i++) {
                        String A = o1.get(i);
                        String B = o2.get(i);
                        for (int j = 0; j < A.length(); j++) {
                            if (A.charAt(j) != B.charAt(j)) {
                                return A.charAt(j) - B.charAt(j);
                            }
                        }
                    }
                    return 0;
                }
            });

            for (int i = 0; i < N; i++) {
                List<String> list = tickets.get(i);
                String from = list.get(0);
                String to = list.get(1);
                arr[i] = new ticket(from, to);
            }
            backtracking(arr);

            List<String> ans = new ArrayList<>();
            List<ticket> tickets1 = res.get(0);
            ans.add("JFK");
            for (ticket ticket : tickets1) {
                ans.add(ticket.to);
            }
            return ans;
        }

        //	1. 假定所有机票至少存在一种合理的行程。
        //	2. 且所有的机票 必须都用一次 且 只能用一次。
        public void backtracking(ticket[] arr) {
            if (list.size() == N) {
                res.add(new ArrayList<>(list));
                return;
            }

            for (int i = 0; i < N; i++) {
                if (used[i]) {
                    continue;
                }
                ticket ticket = arr[i];
                if (list.size() == 0 && !ticket.from.equals("JFK")) {    //	从 "JFK" 开始
                    continue;
                }

                if (list.size() > 0 && !list.get(list.size() - 1).to.equals(ticket.from)) {
                    continue;
                }
                list.add(ticket);
                used[i] = true;
                backtracking(arr);
                if (list.size() == N){
                	return;
				}
                list.remove(list.size() - 1);
                used[i] = false;
            }
        }
    }

    class ticket {
        String from;
        String to;

        public ticket(String from, String to) {
            this.from = from;
            this.to = to;
        }
    }

60. 排列序列

由于数组是有序的,那么我们 backtracking 得到的全排列就是从小到大的顺序的

class Solution {
	boolean[] isVisited;
	List<Integer> list = new ArrayList<>();
	List<List<Integer>> res = new ArrayList<>();
	int count;

	public String getPermutation(int n, int k) {
		count = k;
		isVisited = new boolean[n + 1];
		backtracking(n, k);
		StringBuilder sb = new StringBuilder();
		List<Integer> list = res.get(k - 1);
		for (Integer integer : list) {
			sb.append(integer);
		}
		return sb.toString();
	}

	public void backtracking(int n, int k){
		if (count == 0){  //  只取 k 个
			return;
		}

		if (list.size() == n){
			res.add(new ArrayList<>(list));
			count--;
			return;
		}
		for (int i = 1; i <= n; i++) {  //  全排列
			if (isVisited[i]){	//	访问过了
				continue;
			}
			isVisited[i] = true;
			list.add(i);
			backtracking(n, k);
			list.remove(list.size() - 1);
			isVisited[i] = false;
		}
	}
}

698. 划分为k 个相等的元素

class Solution {
	int count;
    public boolean canPartitionKSubsets(int[] nums, int k) {
    	count = k;
    	int sum = 0;
		for (int num : nums) {
			sum += num;
		}
		if (sum % k != 0){
			return false;
		}
		int[] bucket = new int[k];
		int weight = sum / k;	//	每一个子集和:>= 任何一个数字【最大值】
		Arrays.sort(nums);
		swap(nums, 0, nums.length - 1);  //  逆序排序,便于理解逻辑!!!
		if (weight < nums[0]){
			return false;  //  最大值 > 桶容量
		}
		//	我们可以想象划分子集问题,可以看成向k个桶中放球,每个桶的承重为subSum,而现在nums中存放着重量不一的多个球,如果nums中的球都能放到k个桶中,则可以划分子集成功。
		return backtracking(nums, bucket, 0, weight);
    }

    public boolean backtracking(int[] arr, int[] bucket, int start, int target){
    	if (start == arr.length){	//	所有的球都找到了位置
    		return true;
		}
    	int nowWeight = arr[start];	//	选个球
		for (int i = 0; i < bucket.length; i++) {
			if (i - 1 >= 0 && bucket[i] == bucket[i - 1]){  //  如果所有都相同的话,就一直向右推进【剪枝】
				continue;
			}
			if (nowWeight + bucket[i] <= target){
				bucket[i] += nowWeight;
				if(backtracking(arr, bucket, start + 1, target)){
					return true;
				}
				bucket[i] -= nowWeight;
			}
		}
		return false;
	}
	public void swap(int[] arr, int l, int r){
    	while (l < r){
    		int temp = arr[l];
    		arr[l] = arr[r];
    		arr[r] = temp;
    		l++;
    		r--;
		}
	}
}

数据最节约的备份方法【二分 + 回溯】

import java.util.Scanner;
import java.util.*;


// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        String[] split = in.nextLine().split(",");
        int[] arr = new int[split.length];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = Integer.parseInt(split[i]);
        }
        Arrays.sort(arr);
        reverse(arr, 0, arr.length - 1);
        //  先用二分法求出光盘数量
        int min = 1;
        int max = arr.length;
        int res = 0;
        while (min <= max){
            int mid = min + (max - min) / 2;
            int[] bucket = new int[mid];
            if (backtracking(arr, bucket, 0, 500)){
                res = mid;
                max = mid - 1;  //  尽量取小值
            }else {
                min = mid + 1;
            }
        }
        System.out.println(res);
    }

    public static boolean backtracking(int[] arr, int[] bucket, int start, int target){
        if (start == arr.length){
            return true;
        }
        int nowWeight = arr[start]; //  当前小球
        for (int i = 0; i < bucket.length; i++) {
            if (i + 1 < bucket.length && bucket[i] == bucket[i + 1]){
                continue;
            }
            if (nowWeight + bucket[i] <= target){
                bucket[i] += nowWeight;
                if (backtracking(arr, bucket, start + 1, target)){
                    return true;
                }
                bucket[i] -= nowWeight;
            }
        }
        return false;
    }


    public static void reverse(int[] arr, int l, int r){
        while (l < r){
            int temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            l++;
            r--;
        }
    }
}

叠木块

Ⅰ【每层最多2块 ===> 使用贪心策略,每次都是最大的 或者 最大 + 最小配成】

import java.util.Scanner;
import java.util.*;


// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {

    public static void main(String[] args) {
       Scanner in = new Scanner(System.in);
        String[] split = in.nextLine().split(" ");
        int[] arr = new int[split.length];
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            arr[i] = Integer.parseInt(split[i]);
            sum += arr[i];
        }
        Arrays.sort(arr);
        reverse(arr);
        int res = -1;
        for (int i = arr.length; i >= 1; i--) {
            if (sum % i == 0 && isValid(arr, sum / i)){
                res = i;
                break;
            }
        }
        System.out.println(res);
    }

    public static void reverse(int[] arr){
        int l = 0;
        int r = arr.length - 1;
        while (l < r){
            int temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            l++;
            r--;
        }
    }

    public static boolean isValid(int[] arr, int target){
        int l = 0;
        int r = arr.length - 1;
        if (arr[l] > target){
            return false;
        }
        while (l < r){
            if (arr[l] == target){
                l++;
                continue;
            }
            //  如果不是最大和最小的凑成一对的话,那么最小的就至少有3块,不符合题意
            if (arr[l] + arr[r] == target){
                l++;
                r--;
            }else {
                return false;
            }
        }
        return true;
    }
}

Ⅱ【至少有2层,而且每层可以多个木块】

public class mukuai1 {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        String[] split = in.nextLine().split(" ");
        int[] arr = new int[split.length];
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            arr[i] = Integer.parseInt(split[i]);
            sum += arr[i];
        }
        Arrays.sort(arr);
        reverse(arr);
        int res = -1;
        for (int i = arr.length; i >= 2; i--) {
            int[] bucket = new int[i];
            if (sum % i == 0 && backtracking(arr, bucket, 0, sum / i)){
                res = i;
                break;
            }
        }
        System.out.println(res);

    }

    public static boolean backtracking(int[] arr, int[] bucket, int start, int target){
        if (start == arr.length){
            return true;
        }
        int now = arr[start];

        for (int i = 0; i < bucket.length; i++) {
            if (i + 1 < bucket.length && bucket[i + 1] == bucket[i]){
                continue;
            }
            if (now + bucket[i] <= target){
                bucket[i] += now;
                if (backtracking(arr, bucket, start + 1, target)){
                    return true;
                }
                bucket[i] -= now;
            }
        }
        return false;
    }

    public static void reverse(int[] arr){
        int l = 0;
        int r = arr.length - 1;
        while (l < r){
            int temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            l++;
            r--;
        }

    }
}

计算礼品发放的最小分组数目【也是最多2份】

import java.util.Scanner;
import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
       Scanner in = new Scanner(System.in);
        int target = Integer.parseInt(in.nextLine());
        String[] split = in.nextLine().split(" ");
        Integer[] arr = new Integer[split.length];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = Integer.parseInt(split[i]);
        }
        Arrays.sort(arr, (o1, o2) -> o2 - o1);
        int l = 0;
        int r = arr.length - 1;
        int res = 0;
        while (l < r) {
            if (arr[l] == target || arr[l] + arr[r] > target) {
                l++;
                res++;
                continue;
            }
            res++;
            l++;
            r--;
        }
        
        if (l == r){    //  还剩最后一个的情形
            res++;
        }

        System.out.println(res);
    }
}

313. 迷宫问题【DFS】

import java.util.Scanner;
import java.util.*;


// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    static int row;
    static int col;
    static int[][] arr;
    static List<String> list = new ArrayList<>();
    static List<String> res = new ArrayList<>();
    static int count = Integer.MAX_VALUE;
    static boolean[][] isVisited;


    public static void main(String[] args) {
      Scanner in = new Scanner(System.in);
        row = in.nextInt();
        col = in.nextInt();
        arr = new int[row][col];
         isVisited = new boolean[row][col];
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                arr[i][j] = in.nextInt();
            }
        }

        backtracking1(0, 0);
        for (String re : res) {
            System.out.println(re);
        }
    }

   public static void backtracking1(int i, int j){
       list.add("(" + i + "," + j + ")");  //  当前
        if (i == row - 1 && j == col - 1){
            if (count > list.size()){
                count = list.size();
                res = new ArrayList<>(list);
            }
            return;
        }
        if (i + 1 < row && arr[i + 1][j] == 0 && !isVisited[i + 1][j]) {
            isVisited[i + 1][j] = true;
            backtracking1(i + 1, j);
            list.remove(list.size() - 1);
            isVisited[i + 1][j] = false;
        }
        if (j + 1 < col && arr[i][j + 1] == 0 && !isVisited[i][j + 1]) {
            isVisited[i][j + 1] = true;
            backtracking1(i, j + 1);
            list.remove(list.size() - 1);
            isVisited[i][j + 1] = false;
        }
        if (i - 1 >= 0 && arr[i - 1][j] == 0 && !isVisited[i - 1][j]) {
            isVisited[i - 1][j] = true;
            backtracking1(i - 1, j);
            list.remove(list.size() - 1);
            isVisited[i - 1][j] = false;
        }
        if (j - 1 >= 0 && arr[i][j - 1] == 0 && !isVisited[i][j - 1]) {
            isVisited[i][j - 1] = true;
            backtracking1(i, j - 1);
            list.remove(list.size() - 1);
            isVisited[i][j - 1] = false;
        }
    }
}

324. 最大时间

import java.util.Scanner;
import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    static boolean[] isVisited;
    static List<List<Integer>> res = new ArrayList<>();
    static List<Integer> list = new ArrayList<>();

    public static void main(String[] args) {
       Scanner in = new Scanner(System.in);
        String s = in.nextLine();
        String substring = s.substring(1, s.length() - 1);
        String[] split = substring.split(",");
        int[] arr = new int[split.length];
        isVisited = new boolean[arr.length];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = Integer.parseInt(split[i]);
            if (arr[i] >= 10){  //  1. 超过 10 的数
                System.out.println("invalid");
                return;
            }
        }
        backtracking(arr);
        Collections.sort(res, (o1, o2) -> {
            for (int i = 0; i < o1.size(); i++) {
                if (!o1.get(i).equals(o2.get(i))){
                    return o2.get(i) - o1.get(i);
                }
            }
            return 0;
        });
        if (res.size() == 0){
            System.out.println("invalid");
            return;
        }
        List<Integer> ans = res.get(0);
        System.out.println(ans.get(0) + "" + ans.get(1) +":"+
                ans.get(2) + "" + ans.get(3) +":"+
                ans.get(4) + "" + ans.get(5));
    }
    public static void backtracking(int[] arr){
         if (list.size() == isVisited.length && list.get(0) <= 2 && list.get(2) <= 5 && list.get(4) <= 5){
            if (list.get(0) == 2 && list.get(1) >= 4){  //  第一个数为2,第二个数大于等于 4 时舍弃!!!
                return;
            }
            res.add(new ArrayList<>(list));
            return;
        }
        for (int i = 0; i < arr.length; i++) {
            if (!isVisited[i]){
                list.add(arr[i]);
                isVisited[i] = true;
                backtracking(arr);
                list.remove(list.size() - 1);
                isVisited[i] = false;
            }
        }
    }
}
posted @ 2023-08-29 12:33  爱新觉罗LQ  阅读(155)  评论(0编辑  收藏  举报