【LeetCode】回溯专题

回溯

// 主函数里面先对集合排序
/*
回溯:
void dfs(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素) {	// 横向处理
    	// 对于有重复元素的情况要加上以下判断
    	if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) continue;	
        处理节点;
        dfs(路径,选择列表); // 纵向递归
        回溯,撤销处理结果
    }
}
*/

组合

77. 组合

给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

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

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

    private void dfs(int begin, int n, int k) {
        if (path.size() == k) {
            res.add(new ArrayList<>(path));
            return;
        }
        // 如果for循环选择的起始位置之后的元素个数 已经不足 我们需要的元素个数了,那么就没有必要搜索了。
        // 已经选择的元素个数:path.size();
        // 还需要的元素个数为: k - path.size();
        // n-i+1 >= k - path.size()
        // i <= n+1-(k - path.size())
        // 在集合n中至多要从该起始位置 : n + 1 - (k - path.size()),开始遍历
        for (int i = begin; i <= n + 1 - (k - path.size()); i++) { 
            path.add(i);
            dfs(i + 1, n, k);
            path.remove(path.size() - 1);
        }
    }
}
17. 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

class Solution {
    List<String> res = new ArrayList<>();
    String[] map = {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
    StringBuilder sb = new StringBuilder();

    public List<String> letterCombinations(String digits) {
        if (digits == null || digits.length() == 0) return res;
        dfs(digits, 0);
        return res;
    }

    private void dfs(String digits, int index) {
        if (sb.length() == digits.length()) {	// 递归出口
            res.add(sb.toString());
            return;
        }
        char[] choices = map[digits.charAt(index) - '2'].toCharArray();	// 处理当前层
        for (char ch : now.toCharArray()) {		// 选择本层集合中的元素ch
            sb.append(ch);						// 处理节点
            dfs(digits, index + 1);				// 递归
            sb.deleteCharAt(sb.length() - 1);	// 回溯,撤销上面处理的节点
        }
    }
}
39. 组合总和

给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。

candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。

对于给定的输入,保证和为 target 的唯一组合数少于 150 个。

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

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

    private void dfs(int[] candidates, int begin, int target) {
        if (target == 0) {
            res.add(new ArrayList<>(path));
            return;
        }
        if (target < 0) return;	// 纵向剪枝
        for (int i = begin; i < candidates.length; i++) {
            if (target - candidates[i] < 0) break;   // beat 10% -> 96%,横向剪枝
            path.add(candidates[i]);
            dfs(candidates, i, target - candidates[i]);	// 下一次i位置的元素还可以选
            path.remove(path.size() - 1);
        }
    }
}
⭕️40. 组合总和 II

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次

**注意:**解集不能包含重复的组合。

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

    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        boolean[] used = new boolean[candidates.length];
        dfs(candidates, 0, target, used);
        return res;
    }

    private void dfs(int[] candidates, int begin, int target, boolean[] used) {
        if (target < 0) return;
        if (target == 0) {
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = begin; i < candidates.length; i++) {
            if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) continue;
            if (target - candidates[i] < 0) break;  // beat 14% -> 98%
            path.add(candidates[i]);
            used[i] = true;
            dfs(candidates, i + 1, target - candidates[i], used);
            path.remove(path.size() - 1);
            used[i] = false;
        }
    }
}
216. 组合总和 III

找出所有相加之和为 nk 个数的组合,且满足下列条件:

  • 只使用数字1到9
  • 每个数字 最多使用一次

返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

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

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

    private void dfs(int k, int n, int begin) {
        if (k == 0 && n == 0) {
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = begin; i <= 9; i++) {
            path.add(i);
            dfs(k - 1, n - i, i + 1);
            path.remove(path.size() - 1);
        }
    }
}

排列

46. 全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

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

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

    private void dfs(int[] nums, boolean[] used) {
        if (path.size() == nums.length) {
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            if (used[i] == false) {
                path.add(nums[i]);
                used[i] = true;
                dfs(nums, used);
                path.remove(path.size() - 1);
                used[i] = false;
            }
        }
    }
}
⭕️47. 全排列 II

给定一个可包含重复数字的序列 nums按任意顺序 返回所有不重复的全排列。

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

    public List<List<Integer>> permuteUnique(int[] nums) {
        Arrays.sort(nums);
        boolean[] used = new boolean[nums.length];
        dfs(nums, used);
        return res;
    }

    private void dfs(int[] nums, boolean[] used) {
        if (path.size() == nums.length) {
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            // 在上一题的基础上加了这句判断
            if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) continue;	
            if (used[i] == false) {
                path.add(nums[i]);
                used[i] = true;
                dfs(nums, used);
                path.remove(path.size() - 1);
                used[i] = false;
            }
        }
    }
}

⭕️分割

131. 分割回文串

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。

回文串 是正着读和反着读都一样的字符串。

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

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

    private void dfs(String s, int begin) {
        if (begin == s.length()) {	// 分割到字符串最后结束
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = begin; i < s.length(); i++) {
            if (isReverse(s.substring(begin, i + 1))) {
                path.add(s.substring(begin, i + 1));
                dfs(s, i + 1);
                path.remove(path.size() - 1);
            }
        }
    }

    private boolean isReverse(String s) {
        int len = s.length();
        int left = 0, right = len - 1;
        while (left < right) {
            if (s.charAt(left) != s.charAt(right)) return false;
            left++;
            right--;
        }
        return true;
    }
}
⭐⭐93. 复原 IP 地址

有效 IP 地址 正好由四个整数(每个整数位于 0255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。

  • 例如:"0.1.2.201" "192.168.1.1"有效 IP 地址,但是 "0.011.255.245""192.168.1.312""192.168@1.1"无效 IP 地址。

给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。

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

    public List<String> restoreIpAddresses(String s) {
        if (s.length() > 12) return res;
        dfs(s, 0, 0);
        return res;
    }

    // 相较于上题,这道题限制了组合的次数,也就是规定只能割3次
    private void dfs(String s, int begin, int pointCnt) {
        if (pointCnt == 3) {	// 分割了3次结束
            if (isValid(s.substring(begin, s.length()))) {
                res.add(s);
                return;
            }
        }

        for (int i = begin; i < s.length(); i++) {
            if (isValid(s.substring(begin, i + 1))) {
                s = s.substring(0, i + 1) + '.' + s.substring(i + 1);
                pointCnt++;
                dfs(s, i + 2, pointCnt);
                pointCnt--;
                s = s.substring(0, i + 1) + s.substring(i + 2);
            }
        }
    }

    // 判断是否合法
    private boolean isValid(String s) {
        if (s == null || s.length() == 0) return false;
        if (s.length() > 1 && s.charAt(0) == '0') return false;
        if (s.length() > 4) return false;
        int val = 0;
         for (char c : s.toCharArray()) {
             if (!(c >= '0' && c <= '9')) return false;
             val = val * 10 + (c - '0');
         }
        return val >= 0 && val <= 255;
    }
}

子集

子集问题类似于组合问题,都是使用begin。排列问题使用used数组。

78. 子集

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

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

    public List<List<Integer>> subsets(int[] nums) {
        if (nums == null || nums.length == 0) return res;
        dfs(nums, 0);
        return res;
    }

    private void dfs(int[] nums, int begin) {
        res.add(new ArrayList<>(path));
        for (int i = begin; i < nums.length; i++) {
            path.add(nums[i]);
            dfs(nums, i + 1);
            path.remove(path.size() - 1);
        }
    }
}
90. 子集 II

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

// 在上一题基础上使用used数据处理去重
class Solution {
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();

    public List<List<Integer>> subsetsWithDup(int[] nums) {
        if (nums == null || nums.length == 0) return res;
        boolean[] used = new boolean[nums.length];
        Arrays.sort(nums);
        dfs(nums, used, 0);
        return res;
    }

    private void dfs(int[] nums, boolean[] used, int begin) {
        res.add(new ArrayList<>(path));
        for (int i = begin; i < nums.length; i++) {
            if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue;
            path.add(nums[i]);
            used[i] = true;
            dfs(nums, used, i + 1);
            path.remove(path.size() - 1);
            used[i] = false;
        }
    }
}
⭕️491. 递增子序列

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。

数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

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

    public List<List<Integer>> findSubsequences(int[] nums) {
        if (nums == null || nums.length == 0) return res;
        dfs(nums, 0);
        return res;
    }

    private void dfs(int[] nums, int begin) {
        if (path.size() >= 2) {
            res.add(new ArrayList<>(path));
            // 注意这里不要加return,要取树上的节点
        }
        Set<Integer> set = new HashSet<>(); // 使用set对本层元素进行去重
        for (int i = begin; i < nums.length; i++) {
            if (set.contains(nums[i]) || (!path.isEmpty() && nums[i] < path.get(path.size() - 1))) continue;
            set.add(nums[i]);   // 记录这个元素在本层用过了,本层后面不能再用了,对于本层的其他分支,自然也就不用撤回这个选择
            path.add(nums[i]);
            dfs(nums, i + 1);
            path.remove(path.size() - 1);
        }
    }
}

⭕️棋盘

51. N 皇后

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q''.' 分别代表了皇后和空位。

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

    public List<List<String>> solveNQueens(int n) {
        char[][] board = new char[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                board[i][j] = '.';
            }
        }
        dfs(0, n, board);
        return res;
    }

    private void dfs(int row, int n, char[][] board) {
        if (row == n) {
            res.add(new ArrayList<>(transfer(board)));
            return;
        }
        for (int col = 0; col < n; col++) {
            if (isValid(row, col, n, board)) {
                board[row][col] = 'Q';
                dfs(row + 1, n, board);
                board[row][col] = '.';
            }
        }
    }

    private List<String> transfer(char[][] board) {
        List<String> res = new ArrayList<>();
        for (int i = 0; i < board.length; i++) {
            res.add(String.valueOf(board[i]));
        }
        return res;
    }

    private boolean isValid(int row, int col, int n, char[][] board) {
        // 竖线
        for (int i = 0; i < row; i++) {
            if (board[i][col] == 'Q') return false;
        }
        // 45°
        for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
            if (board[i][j] == 'Q') return false;
        }
        // 135°
        for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
            if (board[i][j] == 'Q') return false;
        }
        return true;
    }
}
37. 解数独

编写一个程序,通过填充空格来解决数独问题。

数独的解法需 遵循如下规则

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

数独部分空格内已填入了数字,空白格用 '.' 表示。

class Solution {
    public void solveSudoku(char[][] board) {
        dfs(board);
    }

    private boolean dfs(char[][] board) {
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                // 对于每一个空的地方尝试插入元素
                if (board[i][j] != '.') continue;
                for (char value = '1'; value <= '9'; value++) {
                    if (isValid(board, i, j, value)) {
                        board[i][j] = value;
                        if (dfs(board)) {
                            return true;
                        }
                        board[i][j] = '.';
                    }
                }
                return false;
            }
        }
        return true;
    }

    // 判断是否可以插入元素value
    private boolean isValid(char[][] board, int row, int col, char value) {
        for (int j = 0; j < 9; j++) {
            if (board[row][j] == value) return false;
        }
        for (int i = 0; i < 9; i++) {
            if (board[i][col] == value) return false;
        }
        int beginRow = (row / 3) * 3, beginCol = (col / 3) * 3;
        for (int i = beginRow; i < beginRow + 3; i++) {
            for (int j = beginCol; j < beginCol + 3; j++) {
                if (board[i][j] == value) {
                    return false;
                }
            }
        }
        return true;
    }
}
36. 有效的数独

请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
class Solution {
    public boolean isValidSudoku(char[][] board) {
        // 记录某行,某位数字是否已经被摆放
        boolean[][] row = new boolean[9][9];
        // 记录某列,某位数字是否已经被摆放
        boolean[][] col = new boolean[9][9];
        // 记录某 3x3 宫格内,某位数字是否已经被摆放
        boolean[][] block = new boolean[9][9];

        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (board[i][j] != '.') {
                    int num = board[i][j] - '1';
                    int blockIndex = i / 3 * 3 + j / 3;
                    if (row[i][num] || col[j][num] || block[blockIndex][num]) {
                        return false;
                    } else {
                        row[i][num] = true;
                        col[j][num] = true;
                        block[blockIndex][num] = true;
                    }
                }
            }
        }
        return true;
    }
}

岛屿问题

200. 岛屿数量

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

class Solution {
    public int numIslands(char[][] grid) {
        int res = 0;
        int m = grid.length, n = grid[0].length;
        boolean[][] visited = new boolean[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == '1' && !visited[i][j]) {
                    dfs(grid, visited, i, j);
                    res++;
                }
            }
        }
        return res;
    }

    private void dfs(char[][] grid, boolean[][] visited, int i, int j) {
        if (!check(grid, i, j) || visited[i][j] || grid[i][j] == '0') return;
        visited[i][j] = true;
        dfs(grid, visited, i + 1, j);
        dfs(grid, visited, i - 1, j);
        dfs(grid, visited, i, j + 1);
        dfs(grid, visited, i, j - 1);
    }

    private boolean check(char[][] grid, int i, int j) {
        int m = grid.length, n = grid[0].length;
        return i >= 0 && i < m && j >= 0 && j < n;
    }
}

不用额外的空间

class Solution {	
	// 不用额外的visited,每次遍历完1后将其置为2表示已访问
    public int numIslands(char[][] grid) {
        int row = grid.length, col = grid[0].length;
        int res = 0;
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (grid[i][j] == '1') {
                    dfs(grid, i, j);
                    res++;
                }
            }
        }
        return res;
    }

    private void dfs(char[][] grid, int i, int j) {
        if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] != '1') return;
        grid[i][j] = '2';
        dfs(grid, i + 1, j);
        dfs(grid, i - 1, j);
        dfs(grid, i, j + 1);
        dfs(grid, i, j - 1);
    }
}
463. 岛屿的周长

给定一个 row x col 的二维网格地图 grid ,其中:grid[i][j] = 1 表示陆地, grid[i][j] = 0 表示水域。

网格中的格子 水平和垂直 方向相连(对角线方向不相连)。整个网格被水完全包围,但其中恰好有一个岛屿(或者说,一个或多个表示陆地的格子相连组成的岛屿)。

岛屿中没有“湖”(“湖” 指水域在岛屿内部且不和岛屿周围的水相连)。格子是边长为 1 的正方形。网格为长方形,且宽度和高度均不超过 100 。计算这个岛屿的周长。

/**
 * 岛屿的周围就是,一个岛屿格子的边与地图边界或者海洋的部分相交的话,那么这条边就是岛屿的周长的一部分。
 dfs即从i,j这个岛屿开始涂色,涂色的时候发现当前是边界或者是海洋,那么当前边就算入周长中,否则的话如果当前已经涂过色,直接return,否则继续递归。
 */
class Solution {
    int res = 0;
    public int islandPerimeter(int[][] grid) {
        if (grid == null || grid.length == 0) return 0;
        int m = grid.length, n = grid[0].length;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == 1) {
                    dfs(grid, i, j);
                }
            }
        }
        return res;
    }

    private void dfs(int[][] grid, int i, int j) {
        if ((i < 0 || i >= grid.length)
            || (j < 0 || j >= grid[0].length)
            || grid[i][j] == 0) {    
                res++;
                return;
        }
        if (grid[i][j] != 1) return;
        grid[i][j] = -1;
        dfs(grid, i, j + 1);
        dfs(grid, i, j - 1);
        dfs(grid, i + 1, j);
        dfs(grid, i - 1, j);
        
    }
}

方法2:

// 迭代判断每个岛屿的四周是否是边界或者海洋
class Solution {
    public int islandPerimeter(int[][] grid) {
        if (grid == null || grid.length == 0) return 0;
        int m = grid.length, n = grid[0].length;
        int res = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                res += getOuter(grid, i, j);
            }
        }
        return res;
    }

    private int getOuter(int[][] grid, int i, int j) {
        int outer = 0;
        if (grid[i][j] == 1) {
            if (i - 1 < 0 || grid[i - 1][j] == 0) outer++;
            if (i + 1 >= grid.length || grid[i + 1][j] == 0) outer++;
            if (j + 1 >= grid[0].length || grid[i][j + 1] == 0) outer++;
            if (j - 1 < 0 || grid[i][j - 1] == 0) outer++;
        }
        return outer;
    }
}
695. 岛屿的最大面积

给你一个大小为 m x n 的二进制矩阵 grid

岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

岛屿的面积是岛上值为 1 的单元格的数目。

计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0

/**
每次dfs进行涂色,涂色前初始化当前涂色区域的面积
如果dfs涂完了,说明这个区域遍历完了,更新最大面积。
*/
class Solution {
    int maxArea = 0;
    int area;

    public int maxAreaOfIsland(int[][] grid) {
        if (grid == null || grid.length == 0) return 0;
        int m = grid.length, n = grid[0].length;
        int res = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == 1) {
                    area = 0;
                    dfs(grid, i, j);
                    maxArea = Math.max(maxArea, area);
                }
            }
        }
        return maxArea;
    }

    private void dfs(int[][] grid, int i, int j) {
        if ((i < 0 || i >= grid.length) || (j < 0 || j >= grid[0].length)||
        grid[i][j] != 1) return;
        area++;
        grid[i][j] = -1;
        dfs(grid, i, j + 1);
        dfs(grid, i, j - 1);
        dfs(grid, i + 1, j);
        dfs(grid, i - 1, j);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值