动态规划(背包九讲)

目录

01背包问题

推导思路

解题代码

朴素代码

优化代码

完全背包问题

推导思路

解题代码

朴素代码

优化代码

多重背包问题1

推导思路

解题代码

朴素代码

优化代码: 

多重背包问题2

推导思路

解题代码

朴素代码:

优化代码:

多重背包问题3

推导思路

解题代码

朴素代码:

优化代码:

混合背包问题

推导思路

解题代码

二维费用的背包问题

推导思路

解题代码

朴素代码:

二维优化代码:

分组背包问题

推导思路

解题代码

朴素代码:

有依赖的背包问题

背包问题求方案数

推导思路

解题代码

朴素代码(二维代码):

优化代码(一维优化):

背包问题求具体方案

推导思路

解题代码

朴素代码:

背包问题多组例题


制作不易,点个赞吧!!!

背包问题又叫动态规划选择问题

一般动态规划都可以用闫式dp分析法来解决

 dp问题的学习方法(重点):

比赛的时候做出dp问题,一般不是凭空想出来怎么做,而是我们一起做过类似的dp问题,用之前做过的方法来做dp问题。dp问题是一个以经验为基础的章节,需要我们对各种dp类型都有系统的练习。

优化方法(重点):

优化和分析是分开的,优化是在将状态转移方程求出之后对代码的一个等价变形

01背包问题

01背包问题 - AcWing题库

背包问题的理论基础重中之重是01背包,一定要理解透!

推导思路

有N件物品和一个最多能装体积为 V 的背包第i件物品的体积是volume[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

对于背包问题,有好几种写法,一种是使用二维数组,即dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少

对于01背包问题的二维图像就是这样

 那么对于01背包的分析思路是:

优化方法:

对于该题,在时间上没法优化,只能在空间上进行优化。

先来看看状态转移方程怎么进行优化

f[i][j] = max(f[i-1][j],f[i-1][j-volumw[i]] + value[i])

可以看到,f[i][j]都是从 i-1 转移来的,那么我们就可以将 i 这一维给取消掉。因为我们需要每次从上一个状态转移过来,那么操作 i,j 的时候就需要是上一个状态的,那么如果 j 正向遍历的话,j 每次都是这一状态得到的,为了解决这个问题,可以逆向遍历,那么每次遍历的 j 就是从上一个状态得到的。

for(int i = 1; i <= n; i++)
        for(int j = vs; j >= v[i]; j--)
            f[j] = max(f[j],f[j-v[i]] + value[i]);

解题代码

朴素代码

#include<iostream>
using namespace std;
int v[1005];
int value[1005];
int f[1005][1005];
int main (void)
{
    int n,vs;
    cin >> n >> vs;
    for(int i = 1; i <= n; i++) cin >> v[i] >> value[i];
    for(int i = 1; i <= n; i++)
    {
        for(int j = 0; j <= vs; j++)
        {
            f[i][j] = f[i-1][j];
            if(j >= v[i]) f[i][j] = max(f[i][j],f[i-1][j-v[i]] + value[i]);
        }
    }
    cout << f[n][vs];
    return 0;
}

优化代码

#include<iostream>
using namespace std;
int v[1005];
int value[1005];
int f[1005];
int main (void)
{
    int n,vs;
    cin >> n >> vs;
    for(int i = 1; i <= n; i++) cin >> v[i] >> value[i];
    for(int i = 1; i <= n; i++)
        for(int j = vs; j >= v[i]; j--)
            f[j] = max(f[j],f[j-v[i]] + value[i]);
    cout << f[vs];
    return 0;
}

完全背包问题

完全背包问题 - AcWing题库

推导思路

有N件物品和一个最多能被重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品可以无限用,求解将哪些物品装入背包里物品价值总和最大。

闫式dp分析法:

那么可以看到,要求 f[i,j]  的最大值要求三重循环,又因为n(1~1000),那么三重循环一定会超时,那么就需要进行优化,怎么进行优化:

观察下面两个式子的关系

f[i][j] = max(f[i-1][j],f[i-1][j-v]+w,f[i-1][j-2v]+2w,...)

f[i][j-v] = max(f[i-1][j-v],f[i-1][j-2v]+w,f[i-1][j-3v]+2w...)

我们可以看到得到:

f[i,j] = max(f[i-1][j],f[i][j-v]+w);

那么我们就可以将该代码优化为二维计算。

优化代码:

还可以将二维代码优化为一维的:

还是那句话,代码优化的方法就是对原代码进行一个等价的变形;

可以先进行变换,然后看和原代码有什么关系。

for(int i = 1; i <= n; i++)
{
    for(int j = 0; j <= m; j++)
    {
        f[i][j] = f[i-1][j];
        //f[j] = f[j] 无区别
        if(j >= volume[i]) f[i][j] = max(f[i][j],f[i][j-volume[i]] + value[i]);
        //                 f[j] = max(f[j],f[j-volume[i]]+value[i]) 无区别
    }
}

可以观察到该代码和01背包优化之后的代码很像,只是改了一下 j 的遍历顺序,为什么会这样。

可以观察到,对于完全背包问题, f[i][j-volume[i]] + value[i]这语句是从 i 进行计算的,所以说,我们进行每次进行计算的时候是从这一层状态进行计算的,所以可以正向进行,而01背包是每次进行计算的时候是从上一层状态进行计算的,所以要逆向进行计算。

解题代码

朴素代码

#include<iostream>
using namespace std;
int volume[1005],value[1010],f[1010][1010];

int main (void)
{
    int n,m;
    cin >> n >> m;
    for(int i =1 ; i <= n; i++) cin >> volume[i] >> value[i];
    
    for(int i = 1; i <= n; i++)
    {
        for(int j = 0; j <= m; j++)
        {
            f[i][j] = f[i-1][j];
            if(j >= volume[i]) f[i][j] = max(f[i][j],f[i][j-volume[i]] + value[i]);
        }
    }
    cout << f[n][m];
    return 0;
}

优化代码

#include<iostream>
using namespace std;
int volume[1005],value[1010],f[1010];

int main (void)
{
    int n,m;
    cin >> n >> m;
    for(int i =1 ; i <= n; i++) cin >> volume[i] >> value[i];
    
    for(int i = 1; i <= n; i++)
        for(int j = volume[i]; j <= m; j++)
            f[j] = max(f[j],f[j-volume[i]] + value[i]);
    cout << f[m];
    return 0;
}

多重背包问题1

多重背包问题 I - AcWing题库

推导思路

有N种物品和一个最多能被重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。第i件物品的个数是s[i]个,求解将哪些物品装入背包里物品价值总和最大。

依然是闫式dp分析法:

由推导公式可以看到,如果想要进行计算需要进行三重循环,观察这题的数据范围可以看到,这题可以进行三重循环进行计算,那么就可以直接进行计算。

解题代码

朴素代码

#include<iostream>
#include<cstring>
using namespace std;
int volume[105],value[105],s[105];
int f[105][105];
int main (void)
{
    int n,m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> volume[i] >> value[i] >> s[i];
    
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
        {
            f[i][j] = f[i-1][j];
            for(int k = 1; k <= s[i]; k++)
            {
                if(j - k * volume[i] >= 0) f[i][j] = max(f[i][j],f[i-1][j-k * volume[i]] + k * value[i]);
            }
        }
    }
    cout << f[n][m] << endl;
    
    return 0;
}

优化代码: 

该题的优化代码就属于多重背包问题2

多重背包问题2

多重背包问题 II - AcWing题库

推导思路

有N种物品和一个最多能被重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。第i件物品的个数是s[i]个,求解将哪些物品装入背包里物品价值总和最大。

与上一题的区别就是数据范围变大了

那么就不能用三重循环进行计算,那么就需要在时间上进行优化,那么怎么进行优化呢?

可以考虑将多重背包问题转化为01背包问题

转化方法:

可以将一种类型的多重背包打包为多种类型的01背包,就是用组合数的原理进行打包。例如:

背包数量是10

那么就可以将该类型的背包转化为一种类型的多个背包。

那么可以转化为1 2 4 3个背包。

组合数原理我写在另一个博客中了,不懂的可以自行观看:

组合数问题

那么我们可以计算,一种类型的物品最多2000个物品,那么可以最多可以拆成log_2(2000)  = 11个物品。

通过计算,可以得到时间复杂度最多为1000 * 2000 * 11 = 2 * 10^6 ,时间复杂度是能过的。

假设成立,接下来就是实现了,因为该题如果直接用二维的话,空间也会超,那么直接用01背包的优化后的思路进行计算该题

解题代码

朴素代码:

#include<iostream>
#include<vector>
using namespace std;
int f[2005];
struct node{
    int volume,value;
};
int main (void)
{
    vector<node> item;
    int n,m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        int v,w,s;
        cin >> v >> w >> s;
        for(int j = 1; j <= s; j++)
        {
            item.push_back({j*v,j*w});
            s -= j;
        }
        if(s > 0) item.push_back({s*v,s*w});
    }
    for(node i : item)
    {
        for(int j = m; j >= i.volume; j--)
        {
			f[j] = max(f[j],f[j-i.volume] + i.value);
        }
    }
    cout << f[m];

    return 0;
}

优化代码:

 优化的代码就是对应的是多重背包问题3了

多重背包问题3

多重背包问题 III - AcWing题库

推导思路

有N种物品和一个最多能被重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。第i件物品的个数是s[i]个,求解将哪些物品装入背包里物品价值总和最大。

依然是对于多重背包的优化过程。

上一个写法是将多重背包转化为01背包求解,这一种写法是将多重背包转化为完全背包的方法来优化。

多重背包的原始状态转移方程:

f[i,j] = max(f[i-1][j],f[i-1][j-v]+w,f[i-1][j-2v]+2w,...,f[i-1][j-si*v]+si*w)

考虑用完全背包的优化方式来优化这个方程 

f[i,j-v] = max(f[i-1][j-v],f[i-1][j-2v]+w,f[i-1][j-3v]+2w,...,f[i-1][j-(si-1)*v]+si*w)

我们发现好像并没有什么用,因为:

因为 完全背包 是一口气把所有体积全部用掉

然而 多重背包 对于每个物品的个数是有限制的 

但是,我们可以把这个式子 继续 推导下去,直到背包体积被用到不能再用为止

也可以理解为 完全背包 下把当前物品 选到不能再选后,剩下的余数得到f(i,r) = f(i-1,r),我们再利用 完全背包优化思路 往回倒推一遍,会惊奇的发现一个 滑动窗口求最大值 的模型

其中 r = j % vi

具体如下:

该图来自acwing中一位大佬的推导

由此可以推出可以用单调队列进行优化。

时间复杂度为O(n * m)

不理解的话,可以去看看大佬的题解

  一只野生彩色铅笔

解题代码

朴素代码:

#include<iostream>
#include<cstring>
using namespace std;
const int N = 1005,M = 20010;
int volume[N],value[N],s[N];
int q[M];
int f[N][M];
int main ()
{
    int n,m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> volume[i] >> value[i] >> s[i];
    
    for(int i = 1; i <= n; i++)
    {
        for(int j = 0; j < volume[i]; j++)  // 该语句是为了模拟不同的余数对于得到的值。
        {
            int head = 0,tail = -1;
            for(int k = j; k <= m; k+=volume[i])
            {
                while(head <= tail && k - q[head] > s[i] * volume[i]) head++;
                while(head <= tail && f[i-1][q[tail]] + (k - q[tail])/volume[i]*value[i] <= f[i-1][k]) tail--;
                q[++tail] = k;
                f[i][k] = f[i-1][q[head]] + (k - q[head])/volume[i]*value[i];
            }
        }
    }
    cout << f[n][m] ;
    return 0;
}

优化代码:

 和 01背包 的优化类似,观察到 状态转移方程,对于 i 阶段,只会用到 i-1 层的状态

滚动数组的写法:

#include<iostream>
#include<cstring>
using namespace std;
const int N = 1005,M = 20010;
int volume[N],value[N],s[N];
int q[M];
int f[2][M];
int main ()
{
    int n,m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> volume[i] >> value[i] >> s[i];
    
    for(int i = 1; i <= n; i++)
    {
        for(int j = 0; j < volume[i]; j++)  // 该语句是为了模拟不同的余数对于得到的值。
        {
            int head = 0,tail = -1;
            for(int k = j; k <= m; k+=volume[i])
            {
                while(head <= tail && k - q[head] > s[i] * volume[i]) head++;
                while(head <= tail && f[(i-1) & 1][q[tail]] + (k - q[tail])/volume[i]*value[i] <= f[(i-1) & 1][k]) tail--;
                q[++tail] = k;
                f[i&1][k] = f[(i-1) & 1][q[head]] + (k - q[head])/volume[i]*value[i];
            }
        }
    }
    cout << f[n&1][m] ;
    return 0;
}

混合背包问题

混合背包问题 - AcWing题库

推导思路

混合背包就是将01背包,完全背包,多重背包问题混在一起进行计算。

那么做法可以依照多重背包2的做法进行计算,将01背包的个数就定义为1,将完全背包中无限个物品转化为有限个(在有限个数据中其数据的范围必须为大于总体积的个数),比如:如果总体积为 V 那么就可以定义为无限个就可以转为为 V 个。因为我们可以看到该题的数据为:

 那么就需要进行二进制优化的方式进行减少时间复杂度

当然,该题的空间也是需要优化的,那么我们就可以通过01背包一维优化的方式进行计算。

剩下的细节请看解题代码

解题代码

二进制优化和一维优化后的代码

#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
int f[1010];
struct node{
    int volume,value;
};
int main (void)
{
    vector<node> item;
    int n,m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        int v,w,s;
        cin >> v >> w >> s;
        if(s == -1) s = 1;
        if(s == 0) s = 1000;  // 该数值只要比m大就好了
        for(int j = 1; j <= s; j *= 2)
        {
            item.push_back({j * v,j * w});
            s -= j;
        }
        if(s > 0) item.push_back({s * v,s * w});
    }
    for(auto i : item)
    {
        for(int j = m; j >= i.volume; j--)
        {
            f[j] = max(f[j],f[j-i.volume] + i.value);
        }
    }
    cout << f[m] << endl;
    return 0;
}

二维费用的背包问题

二维费用的背包问题 - AcWing题库

推导思路

有N件物品和一个最多能装重量为 W最多能装体积为V的背包。第i件物品的重量是weight[i],体积为volume[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

该题就是在01背包的基础上加上了一个重量限制,

闫氏dp分析法

 仔细观察可以发现,与01背包问题就多了一重循环,来判断重量的,那么我们就可以依照01背包问题来写该题

 优化方法:

和01背包一样,也是只能从空间上进行优化,优化方法也是和01背包一样。

解题代码

朴素代码:

#include<iostream>
using namespace std;
int f[1010][105][110];
int volume[1005],value[1005],weight[1005];
int main ()
{
    int n,v,m;
    cin >> n >> v >> m;
    for(int i = 1; i <= n; i++) {
        cin >> volume[i] >> weight[i] >> value[i];
    }
    for(int i = 1; i <= n; i++)
    {
        for(int j = 0; j <= v; j++)
        {
            for(int k = 0; k <= m; k++)
            {
                f[i][j][k] = f[i-1][j][k];
                if(j >= volume[i] && k >= weight[i])
                    f[i][j][k] = max(f[i][j][k],f[i-1][j-volume[i]][k-weight[i]] + value[i]);
            }
        }
    }
    cout << f[n][v][m];
    return 0;
}

二维优化代码:

#include<iostream>
using namespace std;
int f[105][110];
int volume[1005],value[1005],weight[1005];
int main ()
{
    int n,v,m;
    cin >> n >> v >> m;
    for(int i = 1; i <= n; i++) {
        cin >> volume[i] >> weight[i] >> value[i];
    }
    for(int i = 1; i <= n; i++)
    {
        for(int j = v; j >= volume[i]; j--)
        {
            for(int k = m; k >= weight[i]; k--)
            {
                f[j][k] = max(f[j][k],f[j-volume[i]][k-weight[i]] + value[i]);
            }
        }
    }
    cout << f[v][m];
    return 0;
}

分组背包问题

分组背包问题 - AcWing题库

推导思路

有N种物品和一个最多能被重量为W 的背包。在每种物品中最多选出一个物品来装入背包中,在不超过背包的最大容量的前提下,求解将哪些物品装入背包里物品价值总和最大。

该题的做法可以参考多重背包问题1。

首先进行闫式dp分析法:

 我们先逐个分析:

给定 n 组物品,每组物品最多选一个出来放入背包,那么每组物品有两种选择,一种是选这组物品中的一个物品,另一种是不选这组物品

不选的话,我们可以分析出:

f[i][j] = f[i-1][j];

如果是选这组物品中的一个物品,那么我们就需要枚举这一组中的每一个物品,看选这个物品得到的价值大还是不选这个物品得到的价值大。

既然要枚举这一组中的每一个物品,这一步是不是特别像多重背包问题1。

分析结束,那么就来看看时间复杂度:

既然是三重循环那么时间复杂度就是O(n^3)

在时间上是肯定能过的。那么接下来就是实现了

解题代码

朴素代码:

#include<iostream>
#include<vector>
using namespace std;
int s[105];
int f[105][105];
struct node{
    int volume,value;
};
vector<node> item[110];
int main ()
{
    int n,m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++) {
        cin >> s[i];
        for(int j = 1; j <= s[i]; j++)
        {
            int v,w;
            cin >> v >> w;
            item[i].push_back({v,w});
        }
    }
    for(int i = 1; i <= n; i++)
    {
        for(int j = 0; j <= m; j++)
        {
            f[i][j] = f[i-1][j];
            for(int k = 0; k < item[i].size(); k++)
            {
                if(j >= item[i][k].volume)
                    f[i][j] = max(f[i][j],f[i-1][j-item[i][k].volume] + item[i][k].value);
            }
        }
    }
    cout << f[n][m];
    return 0;
}

有依赖的背包问题

有依赖的背包问题 - AcWing题库

该题比较难,需要用到树形dp和图论,最近没时间写了,后来再补。

背包问题求方案数

背包问题求方案数 - AcWing题库

推导思路

该题就是在01背包的基础上改变了问题。

该题要求的是输出 最优选法的方案数。

那么怎么求最优选法的方案数:

求得到第 i 层中的一个 m 以内的所有空间的方案数

当体积为 j 的时候我们判断个物品是否能选,当不选的话,那么它的方案数就是 s[i-1][j]

当体积为 j 的时候,的话,那么它的方案数就是 s[i-1][j-volume[i]]

又因为我们选或不选得到的值都是一样的话,那么它的方案数就是两个相加

 那么我们就可以得到这一组语句:

 t = max(f[j],f[j-volume[i]] + value[i]);
 if(t == f[j]) ans += s[j];
 if(t == f[j - volume[i]] + value[i]) ans += s[j - volume[i]];

因为要求的是最优选法的方案数,那么我们就需要先找到最大的那个价值,然后通过最大的那个价值来进行对最优选法的那个方案数进行求和。

解题代码

该题解主要是依照一维讲的,二维的和一维的还是有点区别的,感兴趣的话自己看。

朴素代码(二维代码):

#include<iostream>
#define int long long
using namespace std;
const int mod = 1000000007;
int volume[1010],value[1010];
int f[1010][1010],s[1010][1010];
signed main (void)
{
    int n,m;
    cin >> n >> m;
    int res = 0;
    for(int i = 1; i <= n; i++) {
        cin >> volume[i] >> value[i];
    }
    
    for(int i = 0; i <= m; i++) s[0][i] = 1; // 当空间为0的时候方案数为1
    
    for(int i = 1; i <= n; i++)
    {
        for(int j = m; j >= 0; j--)
        {
            if(j < volume[i])
            {
                s[i][j] = s[i-1][j];
                f[i][j] = f[i-1][j];
            }
            if(j >= volume[i])
            {
                int t = 0;
                int ans = 0;
                t = max(f[i-1][j],f[i-1][j-volume[i]] + value[i]);
                if(t == f[i-1][j]) ans += s[i-1][j];
                if(t == f[i-1][j - volume[i]] + value[i]) ans += s[i-1][j - volume[i]];
                if(ans > mod) ans %= mod;
                f[i][j] = t;
                s[i][j] = ans;
            } 
        }
    }
    cout << s[n][m] << endl;
    return 0;
}

优化代码(一维优化):

#include<iostream>
#define int long long
using namespace std;
const int mod = 1000000007;
int volume[1010],value[1010];
int f[1010],s[1010];
signed main (void)
{
    int n,m;
    cin >> n >> m;
    int res = 0;
    s[0] = 1; // 当空间为0的时候方案数为1
    for(int i = 1; i <= n; i++) {
        cin >> volume[i] >> value[i];
    }
    for(int i = 1; i <= n; i++)
    {
        for(int j = m; j >= volume[i]; j--)
        {
            int t = 0;
            int ans = 0;
            t = max(f[j],f[j-volume[i]] + value[i]);
            if(t == f[j]) ans += s[j];     // 如果该操作为不选,那么就是该空间的方案数就是加上不选的方案
            if(t == f[j - volume[i]] + value[i]) ans += s[j - volume[i]];  // 如果该操作为选,那么那么就是该空间的方案数就是加上选的方案
            if(ans > mod) ans -= mod; // 取模
            f[j] = t; 
            s[j] = ans;
        }
    }
    int maxx = 0;
    for(int i = 0; i <= m; i++) if(f[i] > maxx) maxx = f[i]; // 找到那个最优的选法的值
    for(int i = 0; i <= m; i++)
    {
        if(f[i] == maxx) {
            res += s[i];
            res %= mod;
        }
    }
    cout << res << endl;
    return 0;
}

背包问题求具体方案

背包问题求具体方案 - AcWing题库

推导思路

该题依然是01背包的变形。要求的是满足该条件的最小字典序的方案

那么该题的做法就和01背包有些区别了。

那么我们来分析一下:

要求的是最小字典序的方案。我们就需要从1开始进行枚举,那么我们得到的那个序列就是最小字典序方案。

1开始枚举的话,那么就需要先算出 2~n 所有方案的最优解枚举到 2 的时候我们又需要求出 3~n 所有方案的最优解

这就可以看出我们需要从后往前进行枚举

那么就是代码实现了

for(int i = n; i >= 1; i--)
{
        for(int j = 0; j <= m; j++)
        {
                f[i][j] = f[i+1][j];
                if(j >= volume[i])
                f[i][j] = max(f[i][j],f[i+1][j-volume[i]] + value[i]);
         }
}

这就将dp得到的值求出来了。

那么接下来就是枚举了。从1开始枚举,看取到最大值是否时是否含 1 这个物品,然后依次向下枚举就好了。

更多细节请看代码。

解题代码

朴素代码:

#include<iostream>
#include<vector>
using namespace std;
int volume[1010],value[1010];
int f[1010][1010];
signed main ()
{
    int n,m;
    cin >> n >> m;
    int res = 0;
    for(int i = 1; i <= n; i++) {
        cin >> volume[i] >> value[i];
    }
    for(int i = n; i >= 1; i--)
    {
        for(int j = 0; j <= m; j++)
        {
            f[i][j] = f[i+1][j];
            if(j >= volume[i])
                f[i][j] = max(f[i][j],f[i+1][j-volume[i]] + value[i]);
        }
    }
    int cur =  m;
    for(int i =1 ; i <= n; i++)
    {
        if(volume[i] <= cur && f[i+1][cur-volume[i]] + value[i] == f[i][cur])
        {
            printf("%d ",i);
            cur -= volume[i];
        }
    }
    return 0;
}

背包问题多组例题

最近没时间写了,后来再补

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值