动态规划专题+回溯专题

https://leetcode-cn.com/problems/combination-sum/submissions/

前言

一、动态规划

1. 分割类问题

2.子序列问题

① 连续子数组最大和
在这里插入图片描述

class Solution {
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
        int len = array.size();
        // 初始化要注意,如果len 为1的话,for循环进不去,要保证直接return 第一个数字
        int ret = array[0], sum = array[0];
        for (int i = 1; i < len; i++) {
            if (sum < 0) {
                sum = array[i];
            } else {
                sum += array[i];
            }
            ret = max(ret, sum);
        }
        return ret;
       
    }
};

② 最长公共子串
dp(i,j) =
if (arr1(i) == arr2(j)) {
dp(i,j) = dp(i-1,j-1) + 1
}
ret = max(ret, dp(i,j)); 还可以获取startIndex,最后输出最长公共字符串

③ 最长回文子串
dp(i,j)表示(i-1,j-1)是不是回文子串
i = 0 : len
j = 0 :i
if (arr(i) != arr(j)) {
continue;
} else {
dp(i,j) = dp(j + 1, i - 1) || (i和j的长度小于等于1)
}
不断更新最大长度

④ 最长递增子序列
首先利用动态规划求出最长递增子序列长度,然后开始逆推子序列
dp(i)表示到i(包括i),最长递增子序列长度是多少
i = 0 : len
j = 0 :i
if (arr(i) > arr(j)) {
dp(i) = max(dp(i), dp(j) + 1);
}
maxLen = dp(len);

 for (int i = len - 1; i >= 0; i--) {
            if (dp[i] == maxLis--) {
                ret.push_back(arr[i]);
            }
        }
        reverse(ret.begin(), ret.end());
        return ret;

⑤ NC92 最长公共子序列(二)
主要是 逆推res

class Solution {
public:
    /**
     * longest common subsequence
     * @param s1 string字符串 the string
     * @param s2 string字符串 the string
     * @return string字符串
     */
    /*
    dp(i,j)
    */
    string LCS(string s1, string s2) {
        int len1 = s1.size(), len2 = s2.size();
        if (len1 == 0 || len2 == 0) {
            return "-1";
        }
        vector<vector<int>> dp(len1 + 1, vector<int> (len2 + 1, 0));
        for(int i = 0; i <= len1; i++) {
            for (int j = 0; j <= len2; j++) {
                if (i == 0 || j == 0) {
                    dp[i][j] = 0;
                } else {
                    if (s1[i-1] == s2[j-1]) {
                        dp[i][j] = dp[i-1][j-1] + 1;
                    } else {
                        dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
                    }
                }
            }
        }
        // 逆推res
        string res = "";
        for (int i = len1, j = len2; dp[i][j] >= 1; ) {
            if (s1[i-1] == s2[j-1]) {
                res += s1[i-1];
                i--, j--;
            } else if (dp[i-1][j] >= dp[i][j-1]) {
                i--;
            } else {
                j--;
            }
        }
        reverse(res.begin(), res.end());
        return res.empty() ? "-1" : res;
        // write code here
    }
};

3.背包问题

4.股票交易问题

① 买卖股票的最好时机(一)
只能买卖一次,不断更新最小值和当前值和最小值之间的gap,gap最大的则为最好卖出的时机。

② 买卖股票的最佳时机 II

/*
dp(i,0)表示第i天过去后,手中还有0张股票的最大值
若第i天卖完的话
dp(i,0) = dp(i-1, 1) + prices[i];
若第i天本身就没有股票的话
dp(i,0) = dp(i-2);
dp(i,0) = max(dp(i-1, 1) + prices[i],dp(i-2))

dp(i,1)表示第i天过去后,手中还有1张股票的最大值
dp(i,1) = dp(i-1, 0) - prices[i];
dp(i,1) = dp(i-1,1);
dp(i,1) = max(dp(i-1, 0) - prices[i], dp(i-1,1));
*/

③买卖股票的最佳时机,含有冻结期

/*
    同714
    
    dp[i][0]表示第i天结束后,手里没有股票时,获益最大值
    dp[i][1]表示第i天结束后,手里有1张股票

    dp[i][0]有几种情况:
    第一种是第i个刚卖完,这个时候i-1肯定有股票  dp[i-1][1] + price[i]
    第二种是第i天处于冻结期, dp[i-1][0]
    dp[i][0] = max(dp[i-1][1] + price[i], dp[i-1][0])

    dp[i][1]有两种情况:
    今天有1股,
    有可能是昨天的,今天冻结:dp[i-1][1]
    有可能是今天刚买的,那么昨天肯定不是刚卖完(如果昨天不是刚卖完,昨晚)dp[i-2] - dp[i]
    如果是冻股,昨天刚卖完,dp[][]
    第一种是第i填刚入股, dp[i-1][0] - price[i],
    第二种是第冻股,第二天必须冬天,说明,dp[i-1][0]
    
    dp[i][1] = max(dp[i-1][0] - price[i], dp[i-1][1]);
    返回
    dp[len-1][0]
    注意变量的初始化和越界情况
*/

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n=prices.size();
        vector<vector<int>>dp(n,vector<int>(2));
        dp[0][0]=0;
        dp[0][1]=-prices[0];
        for(int i=1;i<n;i++)
        {
            dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]);
            dp[i][1]=max(dp[i-1][1],(i>1?dp[i-2][0]:0)-prices[i]);
        }
        return dp[n-1][0];
    }
};

④买卖股票的最佳时机,含有手续费

/*
dp(i,j),j取值为0/1,
dp(i,0)表示第i填结束之后手中持股0个.分为两种情况。
1, 第i天确实是冻结期,dp(i-1, 0) 
2, 第i天刚卖完 dp(i-1,1)+prices[i] - fee;
dp(i,0) = max(dp(i-1, 0), dp(i-1,1)+prices[i] - fee)

dp(i,1)表示第i填结束之后手中持股1个.分为两种情况。
1, 第i天确实是冻结期,dp(i-1, 1) 
2, 第i天刚吗买完 dp(i-1,0)-prices[i];
dp(i,1) = max(dp(i-1, 1), dp(i-1,0)-prices[i]);

*/

class Solution {
public:
 int maxProfit(vector<int>& prices, int fee) {
    int len = prices.size();
    if (len == 1) {
        return 0;
    } else if (len == 2) {
        int temp = prices[1] - prices[0] - fee;
        if (temp > 0) {
            return temp;
        } else {
            return 0;
        }
    }
    vector<vector<int>> dp (len, vector<int>(2, 0));
    dp[0][0] = 0, dp[0][1] = -prices[0];
    for (int i = 1; i < len; i++) {
        dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i] - fee);
        dp[i][1] = max(dp[i-1][0] - prices[i], dp[i-1][1]);
    }
    return dp[len-1][0];
}
};

二、DFS和BFS

1.岛屿问题

① 200.岛屿数量(中等)
在这里插入图片描述

class Solution {
public:
    int ret = 0;

    int numIslands(vector<vector<char>>& grid) {
        // 定界
        if (grid.size() == 0) {
            return 0;
        }
        int column = grid.size(), line = grid[0].size();
        
        for (int i = 0; i < column; i++) {
            for (int j = 0; j < line; j++) {
                if (grid[i][j] == '1') {
                    // 每发现一个岛屿,ret++,然后使用dfs将岛屿淹没
                    dfs(grid, i, j);
                    ret ++;
                }
            }
        }
        return ret;
    }

    void dfs(vector<vector<char>> &grid, int x, int y) {
        // 定界
        int column = grid.size(), line = grid[0].size();
        if (x < 0 || x >= column || y < 0 || y >= line) {
            return;
        }

        // 已经是海水, 无需再递归下去
        if (grid[x][y] != '1') {
            return;
        }

        // 已经遍历过的直接被水淹掉,避免维护visitede数组
        grid[x][y] = '0'; 
        dfs(grid, x + 1, y);
        dfs(grid, x - 1, y);
        dfs(grid, x, y + 1);
        dfs(grid, x, y - 1);
    }

};

② 1254.统计封闭岛屿的数目(中等)
在这里插入图片描述

class Solution {
public:
    // 将与(x,y)相接的陆地都变成海水
    void dfs(vector<vector<int>>& grid, int x, int y) {
        if (x < 0 || y < 0 || x >= grid.size() || y >= grid[0].size()) {
            return;
        }
        if (grid[x][y] == 1) {
            return;
        }
        grid[x][y] = 1;
        dfs(grid, x + 1, y);
        dfs(grid, x - 1, y);
        dfs(grid, x, y + 1);
        dfs(grid, x, y - 1);
    }
    
    int closedIsland(vector<vector<int>>& grid) {
        
        int column = grid.size();
        int line = grid[0].size();
        for (int i = 0; i < column; i++) {
            // 上面和下面的岛屿都淹掉
            dfs(grid, i, 0);
            dfs(grid, i, line - 1);
        }
        for (int j = 0; j < line; j++) {
            // 左面和右面的岛屿都淹掉
            dfs(grid, 0, j);
            dfs(grid, column - 1, j);
        }
        int ret = 0;
        for (int i = 0; i < column; i++) {
            for (int j = 0; j < line; j++) {
                if (grid[i][j] == 0) {
                    ret++;
                    dfs(grid, i, j);
                }
            }
        }
        return ret;
    }
};

③ 最大岛屿面积

class Solution {
public:
    int ret = 0;
    int maxAreaOfIsland(vector<vector<int>>& grid) {
          // 定界
        if (grid.size() == 0) {
            return 0;
        }
        int column = grid.size(), line = grid[0].size();
        
        for (int i = 0; i < column; i++) {
            for (int j = 0; j < line; j++) {
                if (grid[i][j] == '1') {
                    // 每发现一个岛屿,ret++,然后使用dfs将岛屿淹没
                    ret  = max(ret, dfs(grid, i, j));
                }
            }
        }
        return ret;

    }
        // 将与(x,y)相接的陆地都变成海水
    int dfs(vector<vector<int>>& grid, int x, int y) {
        if (x < 0 || y < 0 || x >= grid.size() || y >= grid[0].size()) {
            return 0;
        }
        if (grid[x][y] == 0) {
            return 0;
        }
        grid[x][y] = 0;

        return ( dfs(grid, x + 1, y) + 
        dfs(grid, x - 1, y) + 
        dfs(grid, x, y + 1) + 
        dfs(grid, x, y - 1) + 1);
    }
};

④ 子岛屿
在这里插入图片描述

class Solution {
public:
/*
Q:如何判断2中的岛屿是不是子岛屿?
A:从正向角度来看,在2中有的,在1中都有。
从反向来看,在2中有的, 但是在1中没有的,相应的岛屿肯定不是子岛屿。
所以现沉默非子岛屿,然后再计算2中其余的岛屿。
*/
    void dfs(vector<vector<int>>& grid, int x, int y) {
        // 定界
        if(x < 0 || x > grid.size() -1 || y < 0 || y > grid[0].size() -1) {
            return;
        } 
        // 扩展
        if (grid[x][y] == 0) {
            return;
        }
        grid[x][y] = 0;
        dfs(grid, x + 1, y);
        dfs(grid, x - 1, y);
        dfs(grid, x, y + 1);
        dfs(grid, x, y - 1);
    }


    int countSubIslands(vector<vector<int>>& grid1, vector<vector<int>>& grid2) {
    
        int column = grid1.size();
        int line = grid1[0].size();
        // 沉没非子岛屿
        for (int i = 0; i < column; i++) {
            for (int j = 0; j < line; j++) {
                if (grid1[i][j] == 0 && grid2[i][j] == 1) {
                    dfs(grid2, i, j);
                }
            }
        }

        // 计算子岛屿数目
        int ret = 0;
        for (int i = 0; i < column; i++) {
            for (int j = 0; j < line; j++) {
                if (grid2[i][j] == 1) {
                    ret++;
                    dfs(grid2, i, j);
                }
            }
        }
        return ret;

    }
};

④ 统计不同形状的岛屿数量

class Solution {
public:
/*
如何计算呢?
使用string计算路径。上下左右分布是1,2,3,4 反向分别是-1-2-3-4
set<string>st;
st.insert()
最终计算st.size();
1,使用string来表示对应的路径,还有回溯的思想,使用-dir表示已经回头。
2,使用set去重,因此set.size可以直接表示ret
*/
    void dfs(vector<vector<int>>& grid, int x, int y, string &str, string dir) {
        // 定界
         if(x < 0 || x >= grid.size() || y < 0 || y >= grid[0].size()) {
            return;
        } 
        if (grid[x][y] == 0) {
            return;
        }
        grid[x][y] = 0;
        str += dir;

        dfs(grid, x + 1, y, str, "1");
        dfs(grid, x - 1, y, str, "2");
        dfs(grid, x, y + 1, str, "3");
        dfs(grid, x, y - 1, str, "4");

        str += "-";
        str += dir;
    }
    int numDistinctIslands(vector<vector<int>>& grid) {
        int column = grid.size();
        int line = grid[0].size();
        set<string> st1;
        for (int i = 0; i < column; i++) {
            for (int j = 0; j < line; j++) {
                if (grid[i][j] == 1) {
                    string str = "";
                    dfs(grid, i, j, str, "0");
                    st1.insert(str);
                }
            }
        }
        
        return st1.size();
    }
};

2.橘子腐烂问题

在这里插入图片描述

class Solution {
public:
    int orangesRotting(vector<vector<int>>& grid) {
        if (grid.size() == 0) {
            return 0;
        } 

        // 1, 遍历当前数组中的腐烂句子
        for (int i = 0; i < grid.size(); ++i) {
            for (int j = 0; j < grid[i].size(); ++j) {
                if (grid[i][j] == 2)
                    dfs(grid, i, j, 2);
            }
        }
        
        // 2, 计算形成最后的画面需要的最长时间
        int maxTime = 0;
        for (int i = 0; i < grid.size(); ++i) {
            for (int j = 0; j < grid[i].size(); ++j) {
                if (grid[i][j] == 1) {
                    return -1;
                } else {
                    maxTime = max(maxTime, grid[i][j]);
                }
            }
        }
        return maxTime == 0 ? 0 : maxTime - 2;
    }

    void dfs(vector<vector<int>>& grid, int x, int y, int time) {
        // 基本定界
        if (x < 0 || x >= grid.size() || y < 0 || y >= grid[0].size()) {
            return;
        }
        
        // 如果grid不是新鲜橘子而且到这里腐烂的时间<time,说明之前已经遍历过了
        if (grid[x][y] != 1 && grid[x][y] < time) {
            return;
        }
        grid[x][y] = time;

        dfs(grid, x + 1, y, time + 1);
        dfs(grid, x - 1, y, time + 1);
        dfs(grid, x, y + 1, time + 1);
        dfs(grid, x, y - 1, time + 1);
    }
};

3. 剑指 Offer 34. 二叉树中和为某一值的路径

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
vector<vector<int>> ret;
    void dfs(TreeNode* root, vector<int>&vec, int target) {
        if (!root) {
            return;
        }
        vec.push_back(root->val);
        if (!root->left && !root->right && root->val == target) {
            ret.push_back(vec);
            /*
                1,  return; 这里是不能直接return的,需要将后面的数据pop出来。
                2, 这里不能直接pop,因为缩窄了pop的范围
            */
        }
        dfs(root->left, vec, target - root->val);
        dfs(root->right, vec, target - root->val);
        vec.pop_back();
    }
    vector<vector<int>> pathSum(TreeNode* root, int target) {
        vector<int>vec;
        dfs(root, vec, target);
        return ret;

    }
};

4. 257. 二叉树的所有路径

根节点-叶子节点

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<string>ret;

    void dfs(TreeNode* root, string str) {
        if (!root) {
            return;
        }
        str += to_string(root->val);
        // 叶子节点,将str推入ret中,否则 str+->进行递归
        if (!root->left && !root->right) {
            ret.push_back(str);
        } else {
            str += "->";
            dfs(root->left, str);
            dfs(root->right, str);

        }
    }
    vector<string> binaryTreePaths(TreeNode* root) {
        dfs(root, "");
        return ret;

    }
};

5. 257. 二叉树最大路径和

不设定起始位置

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param root TreeNode类 
     * @return int整型
     dfs:返回当前子树能向父节点所能贡献的最大值
     有三种情况
     1,只有当前节点
     2,当前节点和左子树
     3,当前节点和右子树
     对应的前面三种选择,选择最大的
     */
    int ret = INT_MIN;
    
    int dfs(TreeNode* root) {
        if (!root) {
            return 0;
        }
        int left = max (dfs(root->left), 0);
        int right = max (dfs(root->right), 0); 
        
        ret = max(ret, root->val + left + right);
        return root->val+max(left,right);
    }
    int maxPathSum(TreeNode* root) {
        // write code here
        if(!root) {
            return 0;
        }
        dfs(root);
        return ret;
    }
};

6. 根节点到叶子节点之和

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int ret = 0;
    void dfs(TreeNode* root, int sum) {
        if (!root) {
            return;
        }
        sum = sum * 10 + root->val;
        if (!root->left && !root->right) {
            ret += sum;
            return;
        }
        dfs(root->left, sum);
        dfs(root->right, sum);
    }
    
    int sumNumbers(TreeNode* root) {
        dfs(root, 0);
        return ret;
    }
};

三、回溯

全排列

class Solution {
    vector<vector<int>> ret;
public:
    
    void dfs(vector<int>& nums, int s) {
        if (s == nums.size()) {
            ret.push_back(nums);
            return;
        }
        for(int i = s; i < nums.size(); i++) {
            swap(nums[i], nums[s]);
            dfs(nums, s + 1);
            swap(nums[i], nums[s]);
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        
        dfs(nums, 0);
        return ret;

    }
};

括号生成

class Solution {
public:
    vector<string> ret;

    void dfs(int left, int right, string out) {
        // 递归终止条件(剩余左括号个数>剩余右括号个数)
        if (left > right) {
            return;
        }
        // 获取一条正确的路径
        if (left == 0 && right == 0) {
            ret.push_back(out);
            return;
        }
        // 左右递归。
        if (left > 0) {
            dfs(left - 1, right, out + "(");
        }

         if (right > 0) {
            dfs(left, right - 1, out + ")");
        }
    }
    vector<string> generateParenthesis(int n) {
        dfs(n, n, "");
        return ret;
    }
};

子集

class Solution {
public:
    vector<vector<int>> ret;
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<int>path;
        dfs(nums, 0, path);
        return ret;
    }

    void dfs(vector<int>& nums, int start, vector<int> &path) {
        ret.push_back(path);
        for (int i = start; i < nums.size(); i++) {
            path.push_back(nums[i]);
            // 上面path push了nums[i],下面就是从i + 1 开始了。
            dfs(nums, i + 1, path);
            path.pop_back();
        }
    }
};

[路径总和] 从root到叶子节点

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> ret;
    void dfs(TreeNode* root, vector<int> &path, int targetSum) {
        if (!root) {
            return;
        }
        path.push_back(root->val);
        targetSum -= root->val;
        if (!root->left && !root->right && targetSum == 0) {
            ret.push_back(path);
            // return; 这里不能return,因为要pop
        }
        
        dfs(root->left, path, targetSum);

        dfs(root->right, path, targetSum);
        path.pop_back();
    }

    vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
        vector<int>vec;
        dfs(root, vec, targetSum);
        return ret;
    }
};

##494. 目标和

 /*
0-1背包问题,找nums中和为cnt的个数
(sum−neg)−neg=sum−2⋅neg=target
所以sum - target一定是偶数
*/
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
    int len = nums.size(), sum = 0;
    for (int i = 0; i < len; i++)  {
        sum += nums[i];
    }
    int diff = sum - target;
    if (diff < 0 || diff & 2 != 0) { // sum - target一定是偶数
        return 0;
    }
    int neg = diff / 2;
    vector<vector<int>> dp (len + 1, vector<int>(neg + 1, 0));
    dp[0][0] = 1;
    for(int i = 1; i <= len; i++) {
        for (int j = 0; j <= neg; j++) {
            dp[i][j] = dp[i-1][j];
            if (j >= nums[i-1]) {
                dp[i][j] += dp[i-1][j - nums[i-1]];
            }
        }
    }
    return dp[len][neg];
}
};

39. 组合总和

class Solution {
public:
    vector<vector<int>> ret;
    void dfs(vector<int>& candidates, vector<int> &path, int index, int target) {
        if (target < 0) {
            return;
        }
        if (target == 0) {
            ret.push_back(path);
        }

        for (int i = index; i < candidates.size(); i++) {
            if (candidates[i] > target) {
                continue;
            }
            path.push_back(candidates[i]);
            dfs(candidates, path, i, target - candidates[i]);
            path.pop_back();
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<int>vec;
        dfs(candidates, vec, 0, target);
        return ret;

    }
};

从M中找到N个数的全排列问题

在这里插入图片描述

vector<vector<int>>res;

vector<vector<int>> combine(int n, int k) {
    if (k <= 0 || n <= 0) {
        return res;
    }
    
    vector<int> track;
    backtrack(n, k, 1, track);
    return res;
}

void backtrack(int n, int k, int start, vector<int>& track) {
    if(track.size() == k) {
        res.push_back(track);
        return;
    }

    for (int i = start; i < n; i++) {
        track.push_back(i);
        backtrack(n, k, i + 1, track);
        track.pop_back();
    }
}

有重复子数组的全排列

// 回溯
class Solution {
public:
    vector<vector<int>> res;
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vector<int> temp;
        vector<bool> visited(nums.size(), false);  // 标记访问
        DFS(temp, nums, visited);
        return res;
    }
    void DFS(vector<int>& temp, vector<int>& nums, vector<bool>& visited)
    {
        if(temp.size() == nums.size())
        {
            res.push_back(temp);
            return;
        }
        for(int i = 0; i < nums.size(); ++i)
        {
            if(visited[i])  continue;
            // 去重 重点是在去重
            if(i != 0 && nums[i] == nums[i-1] && !visited[i - 1]) continue;
            visited[i] = true;
            temp.push_back(nums[i]);
            DFS(temp, nums, visited);
            temp.pop_back();
            visited[i] = false;   // 回溯
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值