leetcode(17):背包9讲||总结

本文深入解析背包问题的不同类型,包括01背包、完全背包、多重背包等,提供算法思路与优化方法,附带模板代码和练习题,适合算法学习者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

该篇文章从网络搜刮的+自己的理解;
自己在学。
本文主要参考资料——传送门


练习题:
零钱兑换322

01背包


第一讲 01背包问题

这是最基本的背包问题,每个物品最多只能放一次。

第二讲 完全背包问题

第二个基本的背包问题模型,每种物品可以放无限多次。
第三讲 多重背包问题

每种物品有一个固定的次数上限。
第四讲 混合三种背包问题

将前面三种简单的问题叠加成较复杂的问题。
第五讲 二维费用的背包问题

一个简单的常见扩展。
第六讲 分组的背包问题

一种题目类型,也是一个有用的模型。后两节的基础。
第七讲 有依赖的背包问题

另一种给物品的选取加上限制的方法。
第八讲 泛化物品 第九讲 背包问题问法的变化

试图触类旁通、举一反三。
附录一:USACO中的背包问题

给出 USACO Training 上可供练习的背包问题列表,及简单的解答。 附录二:背包问题的搜索解法

除动态规划外另一种背包问题的解法。

1. 01背包问题

1.1 问题描述

题目
有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

1.2 基本思路

  • 基本思路
    这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。

  • 动规
    用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是:f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}

这个地方可以参考 背包九讲——全篇详细理解与代码实现

该方法的时间复杂度为O(N*V)

具体的原理就不在这里介绍,这里主要用来整理归纳实现方法;

  • 未优化代码
//假定 物品数量为N(Num简写 方便记忆),背包容量为V(Volumn简写)
//假定 第i个物品重量和价值分别为 W[i](Weight简写)、C[i](Cost简写)

//初始化
vector< vector<int> > dp(n,vector<int>(V,0)); //所有值初始化为0
//int dp[1000][1000] = 0;

for(int i=1;i<=N;i++)
{
	for(int j=1;j<=V;j++)
	{
		//如果第i个物品 不选的话,前i个商品放入容积为j最大价值 与 前i-1个商品放入背包中的价值一样;
		//此时背包应该没有约束 容量为j
		f[i][j] = f[i-1][j]; 
		if(j>=W[i])//如果当前容量 还够用 则可以选择第i个物品
		{
			//选的话,在前i-1个商品时,背包容量需要为[j-W[i]];
			//此时的f[i][j]在上面已经赋值了f[i-1][j]
			f[i][j] = max(f[i][j],f[i-1][j-W[i]]+C[i]);
		}
	
	}
}

更新顺序图例:
在这里插入图片描述


刷表的时候顺序:
前1个物品放入0的背包时最大价值
前1个物品放入1的背包时最大价值
前1个物品放入2的背包时最大价值
… … … . …
前1个物品放入V的背包时最大价值
|
前2个物品放入1的背包时最大价值
前2个物品放入2的背包时最大价值
前2个物品放入V的背包时最大价值
|
前3个物品放入1的背包时最大价值
前3个物品放入2的背包时最大价值
前3个物品放入V的背包时最大价值
|
前N个物品放入1的背包时最大价值
前N个物品放入2的背包时最大价值
前N个物品放入V的背包时最大价值


  • 优化后的代码
//初始化
//全局变量定义在堆里面,堆里面的值会被初始化为0
int dp[1000][1000]={0};
for(int i=1;i<=N;i++)
{
	for(int j=V;j>=0;j--)
	{
		f[j] = max(f[j],f[j-W[i]]+C[i]);
	}
}

1.3 初始化问题

最优解背包问题中,存在两种问法

  • 有的题目要求"恰好装满背包"时的最优解
  • 有的题目则并没有要求必须把背包装满

这两种问法的区别是在初始化的时候有所不同。

  • 如果是第一种问法,要求恰好装满背包,那么在初始化时除了f[0]为0其它f[1…V]均设为−∞,这样就可以保证最终得到的f[N]f[N]是一种恰好装满背包的最优解。
  • 如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将f[0…V]全部设为0。

可以这样理解:初始化的f数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值为0的nothing“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是−∞了。
如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。

这个小技巧完全可以推广到其它类型的背包问题,后面也就不再对进行状态转移之前的初始化进行讲解。

1.4 模板代码

#include<iostream>
using namespace std;

const int N = 1010;

int v[N], w[N], dp[N];//dp[N][N]

int main(){
    int n, m;    
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        cin >> v[i] >> w[i];
    }
    
    for(int i = 1; i <= n; i++){
        // for(int j = 0; j <= m; j++){
        //     if(j < v[i])
        //         dp[i][j] = dp[i-1][j];
        //     else
        //         dp[i][j]=max(dp[i-1][j], dp[i-1][j-v[i]]+w[i]);
        // }
        
        for(int j = m; j >=v[i]; j--){
            dp[j] = max(dp[j], dp[j-v[i]]+w[i]);
        }
    }
    cout<<dp[m]<<endl;;
    return 0;
}

2 完全背包问题

2.1 题目描述

题目
有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。每件物品数量不限,求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

2.2 基本思路

该方法就是将01背包的j、循环正序写就行;

#include<iostream>
using namespace std;

const int N = 1010;

int v[N], w[N], dp[N];//dp[N][N]

int main(){
    int n, m;    
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        cin >> v[i] >> w[i];
    }
    
    for(int i = 1; i <= n; i++){
        // for(int j = 0; j <= m; j++){
        //     if(j < v[i])
        //         dp[i][j] = dp[i-1][j];
        //     else
        //         dp[i][j]=max(dp[i-1][j], dp[i-1][j-v[i]]+w[i]);
        // }
        
        for(int j = m; j >=v[i]; j--){
            dp[j] = max(dp[j], dp[j-v[i]]+w[i]);
        }
    }
    cout<<dp[m]<<endl;;
    return 0;
}

类比01背包问题;状态转移方程:
在这里插入图片描述


  • 简单小优化
    在这里插入图片描述

但是上面需要循环三类;

for(int i=1;i<=N;i++)
	for(int j=1;j<=M;j++)
		for(int ii = 1;ii<=k;ii++)
			//write code here
			

复杂度就非常高了;


将完全背包问题转换为01背包问题求解
在这里插入图片描述如上图所示,但是并没有改变时间复杂度


二进制转化方法:
在这里插入图片描述在这里插入图片描述在这里插入图片描述

正序倒序举例参考文章

3 多重背包问题

3.1 问题描述

题目
有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。第i件物品共有n[i]件可用,求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

3.2 基本思路

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

3.3 拆分代码

在这里插入图片描述

3.4 模板代码

普通代码:

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    int n,m;
    cin>>n>>m;
    vector<int> f(m+1,0);
    int v,w,s;
    for(int i=1;i<=n;i++)
    {
        cin>>v>>w>>s;
        for(int j=m;j>=0;j--)
        {
            
            for(int ii = 0;ii<=s;ii++)
            {
                if(j>=ii*v)
                f[j] = max(f[j],f[j-ii*v]+ii*w);
            }
            
                }
    }
    cout<<f[m]<<endl;
        return 0;
}

二进制优化代码:

#include<iostream>
#include <vector>
using namespace std;

int main()
{
    int n,m;
    cin>>n>>m;
    vector<int> datav,dataw;
    int vv,ww,ss;
    for(int i=0;i<n;i++)
    {
        cin>>vv>>ww>>ss;
        for(int ii = 1;ii<ss;ii*=2)
        {
            datav.push_back(ii*vv);
            dataw.push_back(ii*ww);
            ss-=ii;
        }
        if(ss>0)
            {
                datav.push_back(ss*vv);
                dataw.push_back(ss*ww);
            }
        
        
    }
    vector<int> f(m+1,0);
    for(int i=0;i<datav.size();i++) // the num of goods should be updated
    {
        for(int j=m;j>=datav[i];j--)
        {
            f[j] = max(f[j],f[j-datav[i]]+dataw[i]);
        }
    }
    cout<<f[m]<<endl;
    return 0;
}

4. 混合背包问题

分组就行;
在这里插入图片描述

4.1 模板代码

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    int n,m;
    cin>>n>>m;
    vector<int> f(m+1,0);
    
    vector<int> v,w,s;
    int tempv,tempw,temps;
     for(int i=0;i<n;i++)
     {
         cin>>tempv>>tempw>>temps;
         
         if(temps == -1)
         {
            s.push_back(1);
            v.push_back(tempv);
         w.push_back(tempw);
         }
         else if(temps>0)
         {
             for(int ii = 1;ii<=temps;ii*=2)
             {
                 s.push_back(ii);
                 v.push_back(tempv*ii);
                 w.push_back(tempw*ii);
                 
                 temps-=ii;
             }
             if(temps>0)
             {
                 s.push_back(temps);
                 v.push_back(tempv*temps);
                 w.push_back(tempw*temps);
             }
         }else
            s.push_back(temps),v.push_back(tempv),
         w.push_back(tempw);
     }
     
     for(int i=1;i<=s.size();i++)
     {
         if(s[i-1] ==0) //完全背包
         {
             for(int j=1;j<=m;j++)
             {
                 if(j>=v[i-1])
                    f[j]=max(f[j],f[j-v[i-1]]+w[i-1]);
             }
         }else //01
         {
             for(int j=m;j>=0;j--)
             {
                 if(j>=v[i-1])
                     f[j] = max(f[j],f[j-v[i-1]]+w[i-1]);
             }
         }
     }
     
     cout<<f[m]<<endl;
     return 0;
}

5. 二维背包问题

多一层循环就行

在这里插入图片描述

5.1 模板代码

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    int n,m,M;
    cin>>n>>m>>M;
    int v,w,c;
    vector<vector<int> > f(m+1,vector<int>(M+1,0));
    for(int i=1;i<=n;i++)
    {
        cin>>v>>w>>c;
        for(int j=m;j>=v;j--)
        {
            for(int k = M;k>=w;k--)
            {
                f[j][k] = max(f[j][k],f[j-v][k-w]+c);
            }
        }
    }
    cout<<f[m][M]<<endl;
    return 0;
}

10. 求方案总数的问题

dd大佬的讲解中最后也有介绍;下面是个截图

在这里插入图片描述

for i=1..N
   for v=0..V
       f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}
       g[i][v]=0
       if(f[i][v]==f[i-1][v])
           inc(g[i][v],g[i-1][v]
       if(f[i][v]==f[i-1][v-c[i]]+w[i])
           inc(g[i][v],g[i-1][v-c[i]])

在这里插入图片描述在这里插入图片描述
伪代码:

  F[0][]0
 
  F[][0]0
 
  G[][ ]1
 
  for i ← 1 to N
 
      do for j ← 1 to V
 
          F[i][j] ← F[i-1][j]
 
          G[i][j] ← G[i-1][j]
 
          if (j >= C[i])
 
              if (F[i][j] < F[i][j-C[i]]+W[i])
 
                  then F[i][j] ← F[i][j-C[i]]+W[i]
 
                       G[i][j] ← G[i][j-C[i]]
 
              else if (F[i][j] = F[i][j-C[i]]+W[i])
 
                  then G[i][j] ← G[i-1][j]+G[i][j-C[i]]
 
  return F[N][V] and G[N][V]
 

    F[0][0]1  
      
        for i ← 1 to N  
      
                do for j ← 0 to V  
      
                if (j < C[i])  
      
                            then F[i][j] ← F[i-1][j]  
      
                else  
      
                          F[i][j] ← F[i-1][j]+F[i][j-C[i]]  
      
        return F[N][V]  

模板代码

        dp[0]=1;
        for(int i=1;i<=N;i++)
        {
            for(int j=v[i];j<=V;j++)
            dp[j]=dp[j]+dp[j-v[i]];
        }

1000. 另外穿插一个找零钱问题

1000.1 题目简介

一般找零钱和背包问题很像;
输入:总钱数,和拥有的金币种类(1元,2元,5元,10元,20元等);
输出:需要的最少硬币数

扩展:
每种硬币只能取一类;
每种硬币可以无限取;
每种硬币有上限(s[i])
有的硬币可以无限取,有个硬币只有一个和多个;

1000.2 基本思路

乍一看和背包问题是一模一样。零钱的背包容量V 就是 金币总数
每种物品的体积v 就是他的面额;需要注意的就是:只能是恰好装满

所需要统计的就是硬币数量因此 f[i] = max(f[i],f[i-v]+1);
具体分析:
定义f[ i ] [ j ] 为第i个硬币恰好装满amount数量的总额

则分为 选 和 不选
选 : f[i][j] = f[i-1][j-v[i]]+1;
不选: f[i][j] = f[i-1][j];

所以讲道理来说;代码应该和背包问题差不多;

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        if(coins.size()==0||amount ==0)
            return 0;
        vector<int> f(amount+1,99999);
        f[0] = 0;
        for(int i=0;i<coins.size();i++)
        {
            for(int j=1;j<=amount;j++)
            {
                if(j>=coins[i])
                    f[j] = min(f[j],f[j-coins[i]]+1);
            }
        }
        if(f[amount]==99999)
            return -1;
        return f[amount];

    }
};

1000.3 华为多维找零钱问题0715

在这里插入图片描述

//华为笔试题0715

int main()
{
    vector<int> coins = {1, 3, 7, 11, 13};
    vector<int> nums = {1,2,3,4,5};
    int m = 30;

    //多维背包问题 --- 二进制优化
    vector<int> v, w;
    for (int i = 0; i <= coins.size(); i++)
    {
        for (int k = 1; k <= nums[i];k*=2)
        {
            v.push_back(k * coins[i]);
            w.push_back(k); //个数也要统计

            nums[i] -= k;
        }
        if(nums[i]>0)
        {
            v.push_back(nums[i] * coins[i]);
            w.push_back(nums[i]);
        }
    }

    vector<int> f(m + 1, 9999);
    f[0] = 0;
    for (int i = 0; i < v.size();i++)
    {
        for (int j = m; j >= v[i];j--)
        {
            f[j] = min(f[j], f[j - v[i]] + w[i]);
        }
    }

    if(f[m] == 9999)
        cout << -1 << endl;
    cout << f[m] << endl;

    return 0;
}

本文参考资料:
1.单调队列优化多重背包问题
2.各种背包问题
3.背包九江
4.背包玖讲
5.背包九江
6.单队列优化多重背包
7. 背包9讲
8. 9讲
9. dd大佬九江
10.哔哩哔哩教学视频

<think>好的,用户让我详细介绍一下C++相关的算法,我需要先理清楚该从哪些方面入手。首先,用户可能对C++的算法基础不太熟悉,或者想系统学习,所以应该从基础开始,逐步深入。要确保覆盖常见的算法类型,并给出实际例子,这样用户更容易理解。 首先,我应该分几个大类来介绍,比如排序算法、搜索算法、图算法、动态规划、字符串处理、数论算法等。每个类别下需要列出常见的算法,并简要说明它们的原理、时间复杂度以及适用场景。同时,用户可能需要代码示例,所以每个算法最好提供一个简单的C++实现,这样他们可以直观地看到如何应用。 接下来,我需要考虑用户可能的背景。如果是初学者,可能需要更详细的解释,避免使用过于专业的术语,或者在必要时进行解释。但用户也可能有一定基础,想系统复习或深入,所以需要平衡深度和广度。比如排序算法中的快速排序和归并排序,需要比较它们的优缺点,以及在不同情况下的表现。 还要注意用户提到的系统级指令,确保所有数学公式正确使用$...$和$$...$$格式。例如,在解时间复杂度时,可能会有像$O(n \log n)$这样的表达式,需要正确格式化。此外,代码部分需要用正确的语法,比如使用代码块,并确保缩进合适。 另外,用户要求用中文回答,结构清晰,所以需要分点说明,每个算法作为一个小节,加上编号和标题。每个小节的结构大致包括:算法原理、时间复杂度、代码示例。这样用户阅读起来更顺畅。 需要验证每个算法的时间复杂度是否正确,例如冒泡排序是$O(n^2)$,而快速排序平均情况下是$O(n \log n)$。同时,代码示例是否简洁正确,比如二分查找的递归和迭代实现是否正确,图算法中的Dijkstra算法是否需要优先队列等。 另外,动态规划部分可能需要举一个经典例子,比如斐波那契数列或者背包问题,这里选择斐波那契可能更简单易懂,但背包问题更实用。需要权衡,可能两个都提一下,但以其中一个为例。 还要考虑覆盖范围是否全面,用户是否需要更多的算法类型,比如贪心算法、分治算法,这些是否应该单独列出还是包含在其他部分里。比如分治可能在排序中的归并和快速排序中提到,贪心算法可以单独作为一个小节,举一个例子如活动选择问题。 字符串处理方面,KMP算法和Trie树是基础且重要的,需要解释清楚原理,并提供代码示例。数论算法中的欧几里得算法和素数筛法也是常见考点,需要准确描述。 最后,总结部分需要概括所学内容,并给出进一步学习的建议,比如推荐书籍或在线资源,帮助用户深入学习。同时提醒实际应用中根据问题特点选择算法,强调理论与实践结合的重要性。 在整个过程中,要确保LaTeX格式正确,代码示例正确无误,结构层次分明,语言简洁明了。检查是否有错别字或语法错误,确保信息准确可靠,特别是算法的时间复杂度和实现细节。例如,Dijkstra算法在实现时是否处理了负权边的问题,需要指出其不适用于负权边的情况,避免用户误解。 可能还需要考虑用户的实际应用场景,比如竞赛、面试还是项目开发,不同场景下重点可能不同。但用户没有明确说明,所以保持一般性介绍,覆盖常见应用场景。例如,排序和搜索算法在各类应用中都很常见,图算法在网络、路径规划中重要,动态规划和字符串处理在算法题中频繁出现。 总之,需要组织一个结构清晰、内容全面、示例准确的回答,帮助用户建立对C++相关算法的系统认识,并能够根据需求进一步深入学习。</think>我将分门别类为您梳理C++常用算法体系,结合代码示例和数学原理说明。以下为结构化解: --- ### 一、排序算法 #### 1. 快速排序 (Quick Sort) - **原理**:分治策略,选取基准值(pivot)进行分区 - **时间复杂度**:平均$O(n \log n)$,最坏$O(n^2)$ ```cpp void quickSort(int arr[], int low, int high) { if (low < high) { int pi = partition(arr, low, high); quickSort(arr, low, pi - 1); quickSort(arr, pi + 1, high); } } int partition(int arr[], int low, int high) { int pivot = arr[high]; int i = low - 1; for (int j = low; j <= high-1; j++) { if (arr[j] < pivot) { i++; swap(arr[i], arr[j]); } } swap(arr[i+1], arr[high]); return i+1; } ``` #### 2. 归并排序 (Merge Sort) - **原理**:分治法+合并有序序列 - **时间复杂度**:稳定$O(n \log n)$ - **空间复杂度**:$O(n)$ ```cpp void merge(int arr[], int l, int m, int r) { int n1 = m - l + 1, n2 = r - m; int L[n1], R[n2]; for (int i=0; i<n1; i++) L[i] = arr[l+i]; for (int j=0; j<n2; j++) R[j] = arr[m+1+j]; int i=0, j=0, k=l; while (i<n1 && j<n2) arr[k++] = (L[i] <= R[j]) ? L[i++] : R[j++]; while (i < n1) arr[k++] = L[i++]; while (j < n2) arr[k++] = R[j++]; } ``` --- ### 二、搜索算法 #### 1. 二分查找 (Binary Search) - **前提**:有序数组 - **时间复杂度**:$O(\log n)$ ```cpp int binarySearch(int arr[], int l, int r, int x) { while (l <= r) { int mid = l + (r - l)/2; if (arr[mid] == x) return mid; if (arr[mid] < x) l = mid + 1; else r = mid - 1; } return -1; } ``` #### 2. 深度优先搜索 (DFS) - **应用场景**:图遍历、路径查找 ```cpp void DFS(int node, vector<bool>& visited, vector<vector<int>>& graph) { visited[node] = true; for (int neighbor : graph[node]) if (!visited[neighbor]) DFS(neighbor, visited, graph); } ``` --- ### 三、图算法 #### 1. Dijkstra算法 - **用途**:单源最短路径(无负权边) - **时间复杂度**:使用优先队列时为$O((V+E)\log V)$ ```cpp void dijkstra(vector<vector<pair<int, int>>>& graph, int src) { priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq; vector<int> dist(graph.size(), INT_MAX); pq.push({0, src}); dist[src] = 0; while (!pq.empty()) { int u = pq.top().second; pq.pop(); for (auto& [v, weight] : graph[u]) { if (dist[v] > dist[u] + weight) { dist[v] = dist[u] + weight; pq.push({dist[v], v}); } } } } ``` --- ### 四、动态规划 #### 1. 背包问题 - **状态转移方程**: $$ dp[i][w] = \max(dp[i-1][w], \ val[i-1] + dp[i-1][w-wt[i-1]]) $$ ```cpp int knapSack(int W, vector<int>& wt, vector<int>& val) { int n = wt.size(); vector<vector<int>> dp(n+1, vector<int>(W+1, 0)); for (int i=1; i<=n; i++) { for (int w=1; w<=W; w++) { if (wt[i-1] > w) dp[i][w] = dp[i-1][w]; else dp[i][w] = max(dp[i-1][w], val[i-1] + dp[i-1][w - wt[i-1]]); } } return dp[n][W]; } ``` --- ### 五、字符串处理 #### 1. KMP模式匹配 - **核心**:构建前缀函数(部分匹配表) ```cpp vector<int> computeLPS(string pattern) { vector<int> lps(pattern.size(), 0); int len = 0; for (int i=1; i<pattern.size(); ) { if (pattern[i] == pattern[len]) lps[i++] = ++len; else len = (len != 0) ? lps[len-1] : i++; } return lps; } ``` --- ### 六、数论算法 #### 1. 欧几里得算法(GCD) - **数学原理**:$ \gcd(a, b) = \gcd(b, a \mod b) $ ```cpp int gcd(int a, int b) { return (b == 0) ? a : gcd(b, a % b); } ``` --- ### 总结建议 1. **实践路线**:从排序/搜索算法入手,逐步过渡到动态规划和图算法 2. **复杂度分析**:始终关注时间/空间复杂度,对比不同场景下的适用性 3. **资源推荐**: - 《算法导论》经典理论参考 - LeetCode/Codeforces 进行实战训练 - C++ STL中的`<algorithm>`库源码学习 实际开发中应根据具体问题特点选择算法,必要时结合多种算法进行优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CoomCon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值