LeetCode 例题精讲 | 17 动态规划如何拆分子问题,简化思路

leetcode 139. 单词拆分 python思路详解 leetcode 139. 单词拆分 首先先阅读一下题干: 给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。 说明: • 拆分时可以重复使用字典中的单词。 • 你可以假设字典中没有重复的单词。 示例 1: 输入: s = “leetcode”,wordDict = [“leet”, “code”] 输出: tru... 阅读详情

8892246bcd89f896623862abe23a57b0.jpeg

在上一篇文章中,我们讲解了「子数组」类动态规划题目的常见技巧。这篇文章继续讲解动态规划问题中的小技巧。今天要讲的是「如何定义多个子问题」。

常规的动态规划问题只需要定义一个子问题即可。然而在某些情况下,把子问题拆成多个会让思路更清晰。如果你没用过这个技巧的话,不妨跟着今天的例题来学习学习。

本篇文章的内容包括:

  • 如何拆分动态规划的子问题

  • 「最长波形子数组」问题的解法

  • 度假问题的解法

  • 多个子问题与二维子问题的转换关系

最长波形子数组

我们用「最长波形子数组」的解题过程来展示定义多个子问题在解题中的作用。

LeetCode 978. Longest Turbulent Subarray 最长波形子数组(Medium)

当 A 的子数组 A[i..j] 满足下列条件之一时,我们称其为波形子数组:

对于 i <= k < j,当 k 为奇数时, A[k] > A[k+1],当 k 为偶数时,A[k] < A[k+1];

或者:对于 i <= k < j,当 k 为偶数时,A[k] > A[k+1] ,当 k 为奇数时, A[k] < A[k+1]。

也就是说,如果比较符号在子数组中的每个相邻元素对之间翻转,则该子数组是波形子数组。

返回 A 的最长波形子数组的长度。

首先我们要明白「波形子数组」的含义。(吐槽一句,官方把 trubulent 翻译成「湍流」,这翻译是给人看的吗?)我们关注的是数组中相邻元素之间的大小关系。如果后一个元素大于前一个元素,则是数组的「上升段」;反之,则是数组的「下降段」。那么,「波形子数组」就是一段交替上升下降的子数组。例如输入 [9, 4, 2, 10, 7, 8, 8, 1, 9] 中, [4, 2, 10, 7, 8] 是其中最长的一段波形子数组。

de1e3ab1567759c3c7bc40380600722c.jpeg
「波形子数组」是一段交替上升下降的子数组

使用单个子问题求解

我们先看看使用传统的单个子问题该怎么求解这道题。

首先,看到题目中的「子数组」字样,我们应当立即想到子数组相关的解题技巧:在定义子问题的时候给子问题加上位于数组尾部的限制。

不理解这个解题技巧的同学,可以回顾我的上一篇文章:

LeetCode 例题精讲 | 16 最大子数组和:子数组类问题的动态规划技巧

使用这个技巧,我们可以这样定义子问题:

子问题 表示「数组 A[0..k) 中,位于数组尾部的最长波形子数组」。

之所以要限制子问题中求的最长波形子数组位于数组尾部,是因为只有数组尾部的波形子数组才可以和新加入的上升/下降段连接起来。

需要注意的是,波形数组的连接是有条件的,需要「上升段」和「下降段」交替出现。如果波形数组的最后一段是「上升」,就需要连接一段「下降」才是合法的波形数组;而如果波形数组的最后一段是「下降」,就需要连接一段「上升」才是合法的波形数组。

e3e9a2c8c060fc745c9e651af067aa0d.jpeg
如果波形子数组的最后一段是「上升」,就需要连接一段「下降」

而如果「上升」之后又是一段「上升」,那么整个波形数组不合法。波形子数组的长度减少到 2(包含最后一个上升段的两个元素)。

823e9e449209932239ce13cc014f8221.jpeg
连续两个「上升」段,无法继续连接,长度退化为 2

当然,如果最后一段既不是上升,也不是下降,而是「水平」段,那这最后一段也是不合法的。波形子数组的长度减少到 1。

fc05370647bf6bb46c028f6b11bfba14.jpeg
新加入的是水平段,无法继续连接,长度退化为 1

那么,我们在写子问题的递推关系时,需要分类讨论。对于子问题 :

  • 如果 波形数组的最后一段是「上升」,且 A[k-1] 和 A[k-2] 之间是「上升」,那么 ;

  • 如果 波形数组的最后一段是「上升」,且 A[k-1] 和 A[k-2] 之间是「下降」,那么 ;

  • 如果 波形数组的最后一段是「下降」,且 A[k-1] 和 A[k-2] 之间是「上升」,那么 ;

  • 如果 波形数组的最后一段是「下降」,且 A[k-1] 和 A[k-2] 之间是「下降」,那么 ;

  • 如果 A[k-1] 和 A[k-2] 之间是「水平」,那么 。

什么?一个看似简单的问题竟然要分这么多情况考虑,是不是看得头都大了?

通常来说,如果你发现子问题的递推关系过于复杂,那可能是子问题定义得不是很好。关键的思路来了: 如果对子问题进行拆分,可以减少很多不必要的分类讨论。

下面,我们尝试拆分子问题,使用多个子问题进行求解。

使用多个子问题求解

既然我们总是要判断波形数组的最后一段是上升还是下降,那我们为何不在子问题定义时就把它们区分开来呢?

我们可以定义两个子问题,分别对应最后一段上升和下降的波形子数组:

  • 子问题 表示:数组 A[0..k) 中,位于数组尾部,且 最后一段为「上升」 的最长波形子数组;

  • 子问题 表示:数组 A[0..k) 中,位于数组尾部,且 最后一段为「下降」 的最长波形子数组。

这样一来,我们的子问题递推关系也变得清晰了起来:

  • 如果 A[k-1] 和 A[k-2] 之间是「上升」,那么 ,;

  • 如果 A[k-1] 和 A[k-2] 之间是「下降」,那么 ,;

  • 如果 A[k-1] 和 A[k-2] 之间是「水平」,那么 ,。

这样,我们就可以写出题解代码了。需要注意的是,既然我们定义了多个子问题,就需要在代码中定义多个 DP 数组。我们直接把 DP 数组命名为 f 和 g,与子问题对应:

public int maxTurbulenceSize(int[] A) {
    if (A.length <= 1) {
        return A.length;
    }

    int N = A.length;
    // 定义两个 DP 数组 f, g
    int[] f = new int[N+1];
    int[] g = new int[N+1];
    f[0] = 0;
    g[0] = 0;
    f[1] = 1;
    g[1] = 1;

    int res = 1;
    for (int k = 2; k <= N; k++) {
        if (A[k-2] < A[k-1]) {
            f[k] = g[k-1] + 1;
            g[k] = 1;
        } else if (A[k-2] > A[k-1]) {
            f[k] = 1;
            g[k] = f[k-1] + 1;
        } else {
            f[k] = 1;
            g[k] = 1;
        }
        res = Math.max(res, f[k]);
        res = Math.max(res, g[k]);
    }
    return res;
}

多个子问题的本质

让我们从 DP 数组的角度来理解动态规划中「定义多个子问题」究竟意味着什么。

请思考一个问题:在「最长波形子数组」问题中,DP 数组是一维的还是二维的?

从子问题的定义来看的话,子问题只有一个参数 ,看起来应该是一维的。不过和普通的一维动态规划问题的不同之处在于,因为有两个子问题 和 ,所以 DP 数组有两个,其中每个是一维的。

我们可以画出 DP 数组的形状来直观地理解。设数组的长度为 ,则 的取值范围是 。DP 数组是两个长度为 的数组,如下图所示。

311fb2cf7b31d0b87b57836517ae70ae.jpeg
将 DP 数组看成两个一维的数组

接下来,我们在 DP 数组中画出子问题的依赖关系。 只依赖于 , 只依赖于 ,那么可以画出子问题的依赖关系为:

0331a90f36c4e48d9e90f87488bb8795.jpeg
DP 数组中子问题的依赖关系

可以看出,两个子问题互相依赖,整体的依赖顺序是从左往右的。

另一方面,我们也可以把 DP 数组看成二维数组。把两个长度为 的数组拼在一起,就得到了一个 的二维数组。

5b984c1dce6a6b10286519250b038e79.jpeg
将 DP 数组看成二维数组

但是,这样的一个 DP 数组和常规的二维动态规划中的 DP 数组不太一样:

第一,DP 数组其中一维的长度为 2,是个常数。计算空间复杂度的话,这个二维 DP 数组的空间复杂度是 ,仍然是一维数组的复杂度级别。

第二,一般的二维动态规划问题(如最长公共子序列、编辑距离这些经典题目),DP 数组的计算顺序既可以是从上往下,也可以是从左往右。而这个 DP 数组根据依赖顺序,计算顺序只能是从左往右,不能先计算第一行()再计算第二行()。

e0225557b5989dad3512d2329cade4af.jpeg
DP 数组的计算顺序

综上,我们可以看出,有多个子问题的动态规划,其维度实际上介于一维和二维之间。本题只定义了两个(常数个)子问题,而如果子问题的数量扩展到了 个,DP 数组的空间复杂度就到达了 ,变成了一个真正的二维动态规划问题。

另一道例题:度假问题

让我们再看一道典型的拆分子问题的动态规划题目,来理解定义多个子问题的技巧。这道题不是来自 LeetCode,而是来自另一个算法网站 AtCoder:AtCoder DP-C. Vacation

题目链接:https://atcoder.jp/contests/dp/tasks/dp_c

Taro 的暑假明天开始,他决定现在制定好暑假的计划。

假期共持续 天。Taro 可以选择在第 天()做以下三件事之一:

  • A:游泳。获得 点快乐指数。

  • B:捉虫。获得 点快乐指数。

  • C:写作业。获得 点快乐指数。

由于 Taro 做一件事情很容易无聊,所以他不能连续两天做同一件事情。

输入包括 以及数组 、、 的内容。

请计算 Taro 总共能获得的最大快乐指数。

这道题目该怎么拆分子问题呢?我们注意到一个关键的题目条件:Taro 不能连续两天做同一件事情。也就是说:

  • 如果 Taro 今天做的是事情 A,那么他明天可以做事情 B 和 C;

  • 如果 Taro 今天做的是事情 B,那么他明天可以做事情 A 和 C;

  • 如果 Taro 今天做的是事情 C,那么他明天可以做事情 A 和 B。

这样的话,我们可以根据 Taro 今天做的是哪件事,定义出三个子问题:

  • 子问题 表示 Taro 在第 k 天做事情 A 的情况下,前 k 天能获得的最大快乐指数;

  • 子问题 表示 Taro 在第 k 天做事情 B 的情况下,前 k 天能获得的最大快乐指数;

  • 子问题 表示 Taro 在第 k 天做事情 C 的情况下,前 k 天能获得的最大快乐指数。

然后我们可以写出子问题间的递推关系:

递推关系为什么是这样的呢?以 的公式为例:

表示表示 Taro 在第 k 天做事情 A 的情况下,前 k 天能获得的最大快乐指数。既然 Taro 在第 k 天做了事情 A,那么他在第 k-1 天就不能做事情 A,只能做事情 B 或 C,对应 和 。也就是说, 是根据 和 求出来的。

和 的公式同理可得。

有了这个递推关系,我们就可以写出题解代码 了:

private static int vacation(int[] a, int[] b, int[] c) {
    int n = a.length;
    int[] f1 = new int[n+1];
    int[] f2 = new int[n+1];
    int[] f3 = new int[n+1];
    f1[0] = 0;
    f2[0] = 0;
    f3[0] = 0;
    for (int k = 1; k <= n; k++) {
        f1[k] = a[k-1] + Math.max(f2[k-1], f3[k-1]);
        f2[k] = b[k-1] + Math.max(f1[k-1], f3[k-1]);
        f3[k] = c[k-1] + Math.max(f1[k-1], f2[k-1]);
    }
    return Math.max(f1[n], Math.max(f2[n], f3[n]));
}

可以看到,题解代码还是非常简洁的。在代码中,f1、f2 和 f3 呈现出一种相互依赖、交替计算的关系。

我们可以用这样一张图来描述这三个子问题之间的关系:

cf1df1d5815d3a8e4cc2f51eaef2a548.jpeg
三个子问题之间的关系

图中的箭头表示子问题间的依赖关系。例如 到 有一条边,表示 依赖于 。而 不依赖于 ,所以 没有到自己的边。

眼尖的同学可能已经看出,这张图实际上展示的是一个状态机。状态机中有 、、 三种状态。如果状态机在第 k-1 天位于状态 ,那么第 k 天的状态无法维持在 ,只能跳到 或 。这对应了「Taro 不能连续两天做同一件事情」的题目条件。

实际上,「状态机」是动态规划中的一种技巧,大名鼎鼎的股票买卖问题就是属于「状态机 DP」。下一篇文章会详细介绍股票问题和状态机 DP。

总结

本文用两道例题展示了动态规划问题中拆解子问题、定义多个子问题的技巧。两道题目虽然分别定义了 2 个、3 个子问题,但是子问题的拆分方式和计算顺序都是非常相似的。把两道题目放在一起对比的话,可以很快理解动态规划定义多个子问题的套路。

著名的股票买卖问题也是一个常见的定义多个子问题的题目。不过由于股票买卖问题中有一个重要的定义「状态机」的步骤,不太适合作为本期的例题。

在下一篇文章中,我会详细讲解股票买卖问题的解题思路,主要是如何在定义多个子问题的基础上加上状态机的推导。敬请期待。

往期文章

我是 nettee,致力于分享面试算法的解题套路,让你真正掌握解题技巧,做到举一反三。我的《LeetCode 例题精讲》系列文章正在写作中,关注我的公众号,获取最新文章。

b4b65484a475253005b5d3e406746c0c.jpeg
Java实现 LeetCode 343 整数拆分(动态规划入门经典) 343. 整数拆分 给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。 示例 1: 输入: 2 输出: 1 解释: 2 = 1 + 1, 1 × 1 = 1。 示例 2: 输入: 10 输出: 36 解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。 说明: 你可以假设 n 不小于 2 且不大于 58。 class Solu... 阅读详情

相关推荐

leetCode-hot100-动态规划专题

leetCode-hot100-动态规划专题

m0_49074706的博客 1365

动态规划

动态规划(英语:Dynamic programming,简称 DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。 动态规划常常适用于有重叠子问题和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法。 动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再根据子问题的解以得出原问题的解。动态规划往往用于优化递归问题,例如斐波那契数列,如果运用递归的方式来求解会重复计算很多相同的子

qq_43604517的博客 477

LeetCode 139. 单词拆分(Word Break) - 动态规划深度解析

本文详细解析LeetCode 139题"单词拆分"的动态规划解法,涵盖核心思路、代码实现、区间理解和性能优化

weixin_44435110的博客 1754

动态规划-子序列问题

子序列问题。最长公共子序列,一个序列中的最长上升子序列(LIS)

lclchong的博客 388

人工智能(AI)简史:推动新时代的科技力量

人工智能(AI,Artificial Intelligence)是计算机科学的一个分支,旨在研究和开发可以模拟、扩展或增强人类智能的系统。它涉及多种技术和方法,包括机器学习、深度学习、自然语言处理(NLP)、计算机视觉、专家系统等。

qq_17153885的博客 1万+

【隐私计算】从本地加法到在线乘法:Beaver Triple如何重塑算术秘密分享的运算效率

本文深入探讨了Beaver Triple在算术秘密分享中的关键作用,显著提升了隐私计算中的乘法运算效率。通过对比加法与乘法的实现差异,详细解析了Beaver Triple的预处理机制和在线计算优化,展示了其在减少通信量和计算时间方面的革命性突破。

weixin_29227585的博客 436

Python二级--命运-3

命运 题目三: 概述: ''' 考生文件夹下存在3个Python源文件,分别对应3个问题,1个文本文件,作为本题目输入数据,请按照 源文件内部说明修改代码,实现以下功能: 《命运》是著名科幻作家倪匡的作品。这里给出《命运》的一个网络版本文件,文件名为“命运.txt”。 ''' ''' 问题3 (10分) :在PY301-3. py文件中修改代码,对“命运. txt”文件进行字符频次统计,将所有字符按照频次 从高到低排序,字符包括中文、标点、英文等符号,但不包含空格和回车。将排序后的字符及频次输出到考生

qq_39451322的博客 2432

`全网最细!! 入门必看Git教程链接:https://gitee.com/all-about-git`

全网 Git 入门教程 gitee.com。如果显示The requested URL returned error: 403错误,那就表示没有和window本地建立token令牌

qq_37855368的博客 3749

动态规划-划分子问题

浅显的描述算法的思路。

yuanyuan87的博客 1038

uses ARM-Compiler ‘Default Compiler Version 5‘ which is not available. MDK5.37安装ARM_Compiler_5

MDK5.37安装ARM_Compiler_5 最后一个AC5编译器,从MDK5.37开始,不再默认安装,需要独立安装 ARMCompiler_506_Windows_x86_b960.zip (80.94MB) 否则使用AC5编译代码报错如下: Build started: Project: Project *** Target 'STM3210C_EVAL' uses ARM-Compiler 'Default Compiler Version 5' which is not available. *

mowwwcom的专栏 2万+

Codesys电子凸轮Cam表实战:从可视化配置到程序化构建

本文详细介绍了Codesys电子凸轮Cam表的实战应用,从可视化配置到程序化构建的全过程。通过包装机和印刷机案例,解析Cam表的基础设置、调试技巧及动态构建方法,帮助工程师快速掌握电子凸轮技术,提升运动控制精度和效率。

weixin_29309785的博客 298

c++定义动态二维数组_LeetCode 例题精讲 | 17 动态规划如何拆分子问题,简化思路...

点击上方“五分钟学算法”,选择“星标”公众号重磅干货,第一时间送达转自面向大象编程在上一篇文章中,我们讲解了「子数组」类动态规划题目的常见技巧。这篇文章继续讲解动态规划问题中的小技巧。今天要讲的是「如何定义多个子问题」。常规的动态规划问题只需要定义一个子问题即可。然而在某些情况下,把子问题拆成多个会让思路更清晰。如果你没用过这个技巧的话,不妨跟着今天的例题来学习学习。本篇文章的内容包括:...

weixin_42501373的博客 205

ImportError: DLL load failed while importing _pydantic_core: 找不到指定的程序对应解决方案

本文主要介绍了ImportError: DLL load failed while importing _pydantic_core: 找不到指定的程序对应解决方案,希望能对使用Python的同学们有所帮助。 文章目录 1. 问题描述 2. 解决方案

weixin_43178406的博客 19万+

【力扣刷题】整数拆分(动态规划)

动态规其基本思想是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解,经分解得到子问题往往不是互相独立的,举个简单的例子:你知道两个1相加等于2,问你三个1相加你是拿前面的两个1相加的结果加上1呢,还是再用1+1+1,你肯定会用前面的那种方法对吧,这就是动态规划,(1+1)就是(1+1+1)的子问题,且并不是相互独立,你得到了(1+1)就好得到(1+1+1)了。可以拆分为1和2,2不拆分的乘积为2,拆分的乘积为1*dp[3-1]也就是1,取不拆分的乘积和拆分的乘积的最大值为2。

m0_67388084的博客 2965

量化交易零基础入门教程

特点 从零开始教编程。靠谱的量化交易学习资源稀少,且具有不讲编程、代码较难、过多等不适合新手等问题,本教程中则会从零开始教编程,解决量化入门过难过编程门槛这一问题。 量化与编程相结合。本教程不仅会教编程,而且会尽量结合量化情景,减少“我要做量化交易为什么要看这些用来培训程序员的东西”的愤懑。 简易精炼,快速入门。尽量只讲最核心最常用的知识,助你快速摆脱想学习量化却处处陌生不知所措的茫然的新手阶段。 读前说明 文后会有自测与自学,包括一些额外的延伸自学内容以及稍有难度的自测题目。 最最最基础的股票.

JanLEE 2886

SST专题(2-5)MMC-SST 输入级的环流抑制

本文研究了模块化多电平换流器(MMC)的数学模型及其环流抑制控制方法。首先建立了MMC等效电路模型,推导了桥臂电流方程和虚拟电动势表达式,提出通过控制桥臂电压修正量来调节内部电流而不影响外部特性。针对MMC特有的二倍频负序环流问题,设计了基于负序旋转坐标变换的环流抑制控制器(CCSC),将时变环流转换为直流量进行控制。通过比例-积分调节和电压前馈补偿,生成内部不平衡压降参考值,最终形成包含功率控制和环流抑制的整体控制结构。仿真结果表明该方法能有效抑制内部环流,同时保持系统稳定运行。

嵌入式老牛的博客 537

动态规划之发现重叠子问题 LeetCode 343 整数拆分

当遇到一个问题,想要使用动态规划的基础是发现重叠子问题 以LeetCode 343 整数拆分 这道题为例 题目 给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。 示例 1: 输入: 2 输出: 1 解释: 2 = 1 + 1, 1 × 1 = 1。 示例 2: 输入: 10 输出: 36 解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。 说明: 你可以假设 n 不小于 2 且不大于 58。 思路 我们解这道题,可以使用递

与 485

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

【算法题】139. 单词拆分-力扣(LeetCode)

比如说:`dp[4]=True`表示`s[:4]`也就是`leet`可以被字典里的单词拼接而成 由于前面的字符(`s[:4]`)可以被拼接而成,所以我们只需要看后面的是否能够被拼接就行了,以此递推下去就行了

Janium的博客 1295

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

【算法竞赛】动态规划·必考(附leetcode100经典练习)

动态规划几乎是所有算法认证和竞赛中必考的一个知识点,将大问题分解成更简单的子问题,对整体问题的最优解决方案取决于子问题的最优解决方案。本文将详细介绍动态规划的知识点和经典例题。

m0_74140776的博客 1731

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

第九章动态规划——整数拆分

动态规划解决整数拆分得到最大值问题,新手也能看懂!!!!

WenJGo的博客 2363

LeetCode 力扣热题100 单词拆分

• j = 0, 1, 2 → s[0:3] = "lee"、s[1:3] = "ee"、s[2:3] = "e",都不在 wordDictSet,跳过。即,如果 s[0:j] 可以被拆分 (dp[j] == true),并且 s[j:i] 也在 wordDict 里,那么 dp[i] 也为 true。• j = 0 → s[0:4] = "leet",在 wordDictSet,且 dp[0] = true,所以 dp[4] = true。• dp[0] = true,表示空字符串可以被拆分。

qq_51956059的博客 1823

【动态规划】算法例题

假设你正在爬楼梯。需要n阶你才能到达楼顶。每次你可以爬1或2个台阶。你有多少种不同的方法可以爬到楼顶呢?n = 22有两种方法可以爬到楼顶。1. 1 阶 + 1 阶2. 2 阶n = 33有三种方法可以爬到楼顶。1. 1 阶 + 1 阶 + 1 阶2. 1 阶 + 2 阶3. 2 阶 + 1 阶方法1:(用递归会超时)

QiAn的博客 1808

动态规划 —— 基础题(力扣343. 整数拆分)

题目描述 给定一个正整数n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积 链接:343. 整数拆分 - 力扣(LeetCode) (leetcode-cn.com) 分析

qq_40472852的博客 648
上一篇: LeetCode 例题精讲 | 16 最大子数组和:子数组类问题的动态规划技巧
下一篇: 一文教你股票买卖问题实用而装逼的解法
nettee
博客等级 码龄14年 15粉丝 20原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值