算法实验二 动态规划

动态规划1

实验题目:减肥的小K2

题目描述:
小K是个苦命的孩子,他的师傅为了多赚钱,以减肥为理由,让他去采药,并说不完成不能吃饭。野地里有许多不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。要求在规定的时间t里,采到的草药的总价值最大。

输入要求:
第一行有2个整数T(1≤T≤1000)和M(1≤M≤100),一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。
接下来的M行每行包括两个在1到100之间包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出要求:
1个整数,表示在规定的时间内可以采到的草药的最大总价值。
实验代码

#include<bits/stdc++.h>
using namespace std;

int main() {
	int t, m, time[101], val[101];
	int final_val[102][1001] = { 0 }; // 动态规划数组,记录子问题的最优解
	cin >> t >> m;
	for (int i = 0;i < m;i++) {
		cin >> time[i] >> val[i];
	}
	for (int i = 1;i <= m;i++) {
		for (int j = 1;j <= t;j++) {
			final_val[i][j] = final_val[i - 1][j]; // 若不选取该药材
			if (j - time[i - 1] >= 0) { //判断在j采摘时间下能否采摘该药材
				int temp = final_val[i - 1][j - time[i - 1]] + val[i - 1];
				if (temp > final_val[i][j]) { //判断采摘后总价值是否会变大
					final_val[i][j] = temp;
				}
			}
		}
	}
	cout << final_val[m][t] << endl;
	return 0;
}

算法分析与知识点:
很明显,这题和01背包问题非常地相似,采集草药的时间可以看做是背包的容量,草药可以看做是放入背包中的物品,由此可以得到和背包一样的递推式:
1、如果当前拥有的时间无法采集到当前的第i个草药,则实现的价值就是当前时间在前i - 1个草药情况下得到的价值
v a l u e [ i ] [ j ] = v a l u e [ i − 1 ] [ j ] value\left[ i \right] \left[ j \right] =value\left[ i-1 \right] \left[ j \right] value[i][j]=value[i1][j]
2、如果当前拥有的时间可以采集到第i个草药,则实现的价值就是采集了当前的草药之后,用总的时间减去当前采集草药用的时间的剩余时间来采集前i - 1个草药获得的价值
再从采集第i个草药和不采集第i个草药中选择一个最大的价值作为当前价值
v a l u e [ i ] [ j ] = max ⁡ ( v a l u e [ i − 1 ] [ j ] , v a l u e [ i − 1 ] [ j − t i m e [ i ] ] + m [ i ] ) value\left[ i \right] \left[ j \right] =\max \left( value\left[ i-1 \right] \left[ j \right] ,value\left[ i-1 \right] \left[ j-time\left[ i \right] \right] +m\left[ i \right] \right) value[i][j]=max(value[i1][j],value[i1][jtime[i]]+m[i])
根据上面的递推关系式可写出相应的动态规划程序。

实验题目:最大连续子段和

题目描述:
给出长度为n的数组,求最大连续子段和, 输出该最大和。

输入要求:
第1行输入一个整数n<50;表示输入数组的大小
第2行输入n个数,中间用空格隔开

输出要求:
最大连续子段和。
实验代码

#include<bits/stdc++.h>
using namespace std;

int main() {
	int n, nums[51];
	int max;
	cin >> n;
	for (int i = 0;i < n;i++)
		cin >> nums[i];
	max = nums[0];//初始令最大值为第一个数
	for (int i = 1;i < n;i++) {
		if (nums[i - 1] > 0)//判断到前一个数为止的最大子序列和是否大于0
			nums[i] += nums[i - 1];//若大于0,则继续扩大子序列
		if (max < nums[i])
			max = nums[i];//更新最大值
	}
	cout << max << endl;
	return 0;
}

算法分析与知识点:
本题可以在输入数组的基础上直接进行动态规划, 表示以第i个元素结尾的最大连续子序列和。根据对应的关系可以得到递推关系式如下:
n u m s [ i ] = { n u m s [ i ]   , n u m s [ i − 1 ] < = 0 n u m s [ i ] + n u m [ i − 1 ]   , n u m s [ i − 1 ] > 0 nums\left[ i \right] =\left\{ \begin{array}{l} nums\left[ i \right] \ ,nums\left[ i-1 \right] <=0\\ nums\left[ i \right] +num\left[ i-1 \right] \ ,nums\left[ i-1 \right] >0\\ \end{array} \right. nums[i]={nums[i] ,nums[i1]<=0nums[i]+num[i1] ,nums[i1]>0

实验题目:数字三角形

题目描述:
数字三角形,从三角形顶部往下走,只能往左下或右下走,求走到最下面时所经过的数字和最大为多少?(下图为n=6时的情况)。
在这里插入图片描述

输入要求:
第1行:整数n(1<=n<=1000)
第2-n+1行:每行若干整数,第i行有i-1个整数,空格分隔。

输出要求:
一行:一个整数,表示所经过数字的最大和。
实验代码

#include<bits/stdc++.h>
using namespace std;

int a[1010][1010];//数字数组
int d[1010][1010];//记录自底向上到(i,j)位置的最大值
int n;
int dfs(int i, int j)
{
    if (d[i][j] > 0)       //说明该点已经计算过直接返回该点的值
        return d[i][j];
    return d[i][j] = a[i][j] + (i == n ? 0 : max(dfs(i + 1, j), dfs(i + 1, j + 1)));
}
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= i; j++)
            cin >> a[i][j];
    }
    memset(d, -1, sizeof(d));  //将d初始化为-1
    printf("%d\n", dfs(1, 1));

    return 0;
}

算法分析与知识点:
本题若直接用递归的方法进行求解,会造成大量子问题的重复求解,最终导致超时。因此可以采用记忆化的递归,该方法也是用的递归,但同时把计算结果保存在数组d中,在计算d[i][j]的之前要先判断它的值是否为-1,如果不是表示这个点已经计算过了,直接放回这个点的值就行,这种方法被称为记忆化,时间复杂度为O(n^2),与直接调用递归去算相比较有了巨大的优化。

动态规划2

实验题目:最长非连续公共子序列

题目描述:
给定两个字符串,求解这两个字符串的最长非连续(允许连续或非连续)的公共子序列的长度(Longest Common Sequence)。
比如字符串1:BDCABA;字符串2:ABCBDAB。
则这两个字符串的最长公共子序列长度为4,最长公共子序列是:BCBA

输入要求:
输入2行,每行一个字符串;字符串长度<1000。
输出要求:
输出两个字符串的最长非连续的公共子序列的长度。
实验代码

#include<bits/stdc++.h>
using namespace std;
int dp[1001][1001] = { 0 }; //记录动态规划结果
string s1, s2;
int findLCS() {
    int n = s1.length(), m = s2.length();
    for (int i = 1;i <= n;i++) {
        for (int j = 1;j <= m;j++) {
            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]);//解两个子问题
            }
        }
    }
    return dp[n][m];
}
int main()
{
    cin >> s1 >> s2;
    int res = findLCS();
    cout << res << endl;

    return 0;
}

算法分析与知识点:
本题主要运用了动态规划的思想,动态规划采用二维数组来标识中间计算结果,避免重复的计算来提高效率。
最长公共子序列的长度的动态规划方程
设有字符串s1[0…n],s2[0…m],下面就是递推公式。字符串s1对应的是二维数组dp的行,字符串s2对应的是二维数组dp的列。
d p [ i ] [ j ] = { 0 ,   i = 0   o r   j = 0 d p [ i − 1 ] [ j − 1 ] + 1 , s 1 [ i ] = s 2 [ j ] max ⁡ ( d [ i ] [ j − 1 ] , d p [ i − 1 ] [ j ] ) , s 1 [ i ] ≠ s 2 [ j ] dp\left[ i \right] \left[ j \right] =\left\{ \begin{array}{l} 0,\ i=0\ or\ j=0\\ dp\left[ i-1 \right] \left[ j-1 \right] +1\text{,}s1\left[ i \right] =s2\left[ j \right]\\ \max \left( d\left[ i \right] \left[ j-1 \right] ,dp\left[ i-1 \right] \left[ j \right] \right) ,s1\left[ i \right] \ne s2\left[ j \right]\\ \end{array} \right. dp[i][j]=0, i=0 or j=0dp[i1][j1]+1s1[i]=s2[j]max(d[i][j1],dp[i1][j]),s1[i]=s2[j]
根据上面递推公式可以写出相应的程序。

实验题目:切钢条

题目描述:
一家公司购买长钢条,将其切割成短钢条出售,切割本身没有成本,长度为i的短钢条的价格为Pi。那给
定一段长度为n的钢条和一个价格表Pi,求钢条的切割方案使得收益Rn最大。
在这里插入图片描述

输入要求:
输入钢条的长度n。
输出要求:
输出获得的最大收益。
实验代码

#include<bits/stdc++.h>
using namespace std;
int pi[11] = { 0,1,5,8,9,10,17,17,20,24,30 }; //记录已知长度钢条价值
int dp[1000] = { 0 };//记录动态规划结果
int findMaxVal(int n)
{
    if (n == 0) // 若n为0直接返回
        return 0;
    for (int i = 1;i <= n;i++) {
        for (int j = 1;j <= i && j <= 10;j++) { // 第一刀最多切10种
            dp[i] = max(pi[j] + dp[i - j], dp[i]);//遍历所有切法
        }
    }
    return dp[n];
}
int main()
{
    int n;
    cin >> n;
    int res = findMaxVal(n);
    cout << res << endl;

    return 0;
}

算法分析与知识点:
本题主要运用动态规划的思想,对于长度为n的钢条,我们可以先切一刀,切下长度为1-10的钢条,共10种切法,最大收益就是切下的钢条收益和剩下钢条的最大收益之和。

  1. 钢条长度为1的最大收益计算出来,保存到dp[1]
  2. 长度为2的钢条,遍历每一种切法,收益最大的保存到dp[2]
  3. 计算长度为n的钢条的最大收益,此时dp数组已经保存了1——n-1长度钢条的最大收益
    遍历这10中切法,就可以找到最大的收益
    由于每种钢条长度的最大收益都被保存在数组dp中,避免了很多的重复计算。
    d p [ i ]   =   max ⁡ ( d [ i ] , d p [ i − j ] + p i [ j ] ) , j = 1...10 dp\left[ i \right] \ =\ \max \left( d\left[ i \right] ,dp\left[ i-j \right] +pi\left[ j \right] \right) ,j=1...10 dp[i] = max(d[i],dp[ij]+pi[j]),j=1...10

动态规划3

实验题目:合格的盗贼

题目描述:
一条街上有N个商铺;商铺i有价值V[i]的物品,你有足够的时间在晚上光顾所有的商店,人们称呼你为盗贼;每个商店都有一个报警器,会在晚上报警,但是只有相邻的2个商店同时报警时,警察才会出动;你需要证明你是个合格的盗贼。
输入要求:
第一行一个整数N<=100,商店数。
第二行N个整数,每个商店的价值

输出要求:
输出偷盗的最大价值。
实验代码

#include<bits/stdc++.h>
using namespace std;
int dp[101] = { 0 };
int findMaxValue(int n) {
    if (n == 1)
        return dp[0];
    else if (n == 2)
        return max(dp[0], dp[1]);
    dp[1] = max(dp[0], dp[1]);
    for (int i = 3;i <= n;i++) {
        dp[i - 1] = max(dp[i - 2], dp[i - 3] + dp[i - 1]); // 动态规划递推关系
    }
    return dp[n - 1];

}
int main()
{
    int n;
    cin >> n;
    for (int i = 0;i < n;i++) {
        cin >> dp[i];
    }
    int res = findMaxValue(n);
    cout << res << endl;
    return 0;
}

算法分析与知识点:
本题主要运用了动态规划的思想,在原先输入数组的及基础上进行动态规划,节约了内存空间。
根据商店的报警机制我们可以列出一下递推关系,dp数组初始为第i家商店的价值vi
d p [ i ] = max ⁡ ( d p [ i − 1 ] , d p [ i − 2 ] + d p [ i ] ) dp\left[ i \right] =\max \left( dp\left[ i-1 \right] ,dp\left[ i-2 \right] +dp\left[ i \right] \right) dp[i]=max(dp[i1],dp[i2]+dp[i])
根据上面递推公式可以写出相应的程序。

实验题目:小莫踩蘑菇

题目描述:
大家都知道提莫队长喜欢种蘑菇。有一天提莫正走在回约德尔国的路上,忽然看到路上长了很神奇的蘑菇,蘑菇会不断从某处长出来,但是如果不快点(1秒内)采走的话会消失。酷爱蘑菇的提莫马上去采蘑菇。说来小提莫的人品实在是太好了,这蘑菇别处都没有,就会长在他(她?它?)身旁的10米范围内。蘑菇如果不马上采走就会坏掉,所以提莫队长马上卸下身上的背包去接。但由于小路两边都不能站人,所以他只能在小路上踩。虽然小莫队长也算约德尔国的短跑健将,但是由于手脚太短,小莫每秒钟还是只能移动1米并且只能踩到1米范围内的蘑菇。
为了使问题简化,可以将小路看作从0-10的一维坐标系,开始时提莫站在5的位置,在第一秒,他只能踩到4,5,6这三个位置中其中一个位置上的蘑菇。
在这里插入图片描述

问提莫最多能采到多少蘑菇?
输入要求:
输入数据有多组。多组数据的第一行以正整数n(0<n<100000),表示有n个蘑菇会出现在这条路上。在接下来的n行中,每行有两个整数x,T(0<T<100000),表示在第T秒有一个蘑菇出现在x点上。同一秒钟在同一个点上可能出现多个蘑菇。## 输出要求:
每一组输入数据对应一行输出。
输出要求
输出一个整数m,表示提莫队长最多可能踩到m个蘑菇。
实验代码

#include <bits/stdc++.h>
using namespace std;
int mushroom[11][100000] = { 0 }; //记录第i秒各个位置蘑菇的生长情况
int dp[11][100000] = { 0 }; // 记录第i秒小莫站在位置j能采到的最多蘑菇量
int main() {
    int n;
    int x, t;
    while (scanf("%d", &n) != EOF) {
        memset(mushroom, 0, sizeof(mushroom)); //初始化清零
        memset(dp, 0, sizeof(dp)); //初始化清零
        for (int i = 0; i < n; i++) {
            scanf("%d%d", &x, &t);
            mushroom[x][t]++;
        }
        dp[5][0] = mushroom[5][0]; // 设置初始条件
        int ans = 0;
        for (int time = 1; time < 100000; time++) {
            for (int p = 0; p < 11; p++) { //根据不同位置来判断第i秒的第j个位置是怎么来的
                if (p == 0) {
                    dp[p][time] =
                        max(dp[p + 1][time - 1], dp[p][time - 1]);
                }
                else if (p == 10) {
                    dp[p][time] =
                        max(dp[p - 1][time - 1], dp[p][time - 1]);
                }
                else {
                    dp[p][time] =
                        max(max(dp[p + 1][time - 1], dp[p][time - 1]),
                            dp[p - 1][time - 1]);
                }
                dp[p][time] += mushroom[p][time];
                if (dp[p][time] > ans)
                    ans = dp[p][time];
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

算法分析与知识点:
本题主要运用动态规划的思想,根据小莫的移动情况来看第i秒j位置的可能有三种情况:分别为不动、从左来、从右来。
d p [ i ] [ j ] = max ⁡ ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − 1 ] , d p [ i − 1 ] [ j + 1 ] ) + m u s h r o o m [ i ] [ j ] dp\left[ i \right] \left[ j \right] =\max \left( dp\left[ i-1 \right] \left[ j \right] ,dp\left[ i-1 \right] \left[ j-1 \right] ,dp\left[ i-1 \right] \left[ j+1 \right] \right) +mushroom\left[ i \right] \left[ j \right] dp[i][j]=max(dp[i1][j],dp[i1][j1],dp[i1][j+1])+mushroom[i][j]
根据上面递推公式可以写出相应的程序。

动态规划-堂练

实验题目:不下降的数字序列

题目描述:
在一个数字序列中,找到一个最长的非连续子序列,使得这个子序列是不下降(非递减)。现有序列A={1,2,3,-1,-2,7,9},则A的最长不下降子序列是{1,2,3,7,9}。
如果有多个最长序列,只需选数字顺位靠后的序列从大到小输出。

输入要求:
输入2行;
第一行一个整数n,表示有n个整数的序列要输入,n<1000;
第二行共有n个整数。

输出要求:
输出最长的不下降子序列,只需选数字顺位靠后的序列从大到小输出。
实验代码

#include<iostream>
#include<algorithm>
using namespace std;
#define N 1000 
//记录当前的最长的非连续子序列的长度
int dp[N];
//存储数据的数组
int a[N];
//存储当前位置元素的下一跳位置
int index[N];
//存储最终结果
int result[N];
int main() {
	int i, j;
	int n;
	cin >> n;	//数组长度
	//读取数据
	for (i = 1; i <= n; i++) {
		cin >> a[i];
	}
	//初始化dp数组,设置数据都为1(自身就是1)
	//初始化index数组
	for (i = 1; i <= n; i++) {
		dp[i] = 1;
		index[i] = 0;
	}
	//从倒数第2个开始(倒数第一个不用记录),记录以其为首到后面的最长非递减子序列的长度
	for (i = n - 1; i >= 1; i--) {
		
		//记录后面某个子序列的最长非递减子序列的长度
		int sub_sequence_length = 0;
		for (j = i + 1; j <= n; j++) {
			//如果后面的元素比前面的元素大,并且其对应的dp值(非递减子序列长度)也大于等于当前的,则进行更新
			if (a[j] >= a[i] && dp[j] >= sub_sequence_length) {
				sub_sequence_length = dp[j];	//更新当前最短长度
				index[i] = j; //记录i的下一跳是j
			}
		}		
		//如果子序列不为0(即后面存在非递减子序列),则对其更新,等于后面非递减子序列的长度加1
		if (sub_sequence_length) {
			dp[i] = sub_sequence_length + 1;
		}
	}
	//找到dp数组中最大的那一个数,其对应的就是最长非递减子序列的第一个元素
	int max_length = 0, p;
	for (i = 1; i <= n; i++) {
		if (dp[i] >= max_length) {
			max_length = dp[i];
			p = i;	//p记录当前位置的下标
		}	
	}
	for (i = 1; i <= max_length; i++) {
		result[i] = a[p];
		p = index[p];
	}
	//进行排序
	// sort(首元素地址,尾元素的下一个地址,比较函数);
	//我们第一个位置没有用,所以首元素地址是result + 1
	sort(result + 1, result + max_length + 1);
	cout << max_length << endl;
	for (i = max_length; i >= 1; i--) {
		if (i == 1) {
			cout << result[i];
		}
		else
			cout << result[i] << " ";
	}
	cout << endl;
	return 0;
}

算法分析与知识点:
思路:设置一个a[]数组保存原始的数据
设置一个dp[]数组,从最后一个数据开始,记录下以其为首的最长非递减子序列的长度
设置一个索引数组index[],记录当前元素的下一跳元素
sort(首元素地址,尾元素的下一个地址,比较函数);
result[]数组记录最终的结果
从倒数第二个开始向前进行推进来更新dp[],更新后的dp[]又会被更前面的用到,全部更新完毕之后,从中选取一个最大的值作为最长非递减子序列的长度
同时Index会保存每个元素的下一跳的位置,保证元素可以被找到,找到之后,将元素存入result[]数组中,进行排序,按从大到小输出。

实验题目:爆破组

题目描述:
暴徒设计了一种便携的链形炸弹,由多个独立能量珠组合而成;由于制造是手工的,因此每个珠子的能量不同;炸弹被启动时,珠子根据控制信号按某一顺序逐一被外层的突刺刺破,刺破的两个珠子的物质融合在一起时,能量不断的聚合增大。
能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数,被称为聚合标记。对于相邻的两颗珠子,前一颗珠子的尾标记一定等于后一颗珠子的头标记。因为只有这样,这两颗珠子才能产生聚合能量。如果前一颗能量珠的头标记为m,尾标记为r,后一颗能量珠的头标记为 r,尾标记为 n,则聚合后释放的能量为m×r×n ,新产生的珠子的头标记为 m,尾标记为 n。
当所有珠子聚合成一起时,就是该炸弹的综合破坏能量。
由于该该炸弹的独特的刺突设计,炸弹不能被移动和解除,只能引爆。唯一减少伤亡的方法是通过暴露在外的信号控制装置,改变信号的控制顺序,从而使其爆炸时的能量最小。请找出引爆的最小能量。

输入要求:
第一行输入珠子数量n,2<n<20
第二行输入n个珠子的头尾标记,相邻头尾相同的标记只输入一个,因此共n+1个标记值。

输出要求:
最小的杀伤能量。
实验代码

#include <bits/stdc++.h>
using namespace std;
#define N 20 
int a[N + 1];
int dp[N + 1][N + 1] = { 0 };
int main() {
	int n;
	cin >> n;
	for (int i = 0;i <= n;i++)
		cin >> a[i];

	for (int r = 2;r <= n;r++) { //包含最小矩阵个数
		for (int i = 0;i < n - r + 1;i++) {
			int j = i + r - 1;  // i,j表示当前考虑范围的起始终结位置
			dp[i][j] = dp[i][i] + dp[i + 1][j] + a[i] * a[i + 1] * a[j + 1]; // 赋初值
			for (int k = i + 1;k < j;k++) {
				int t = dp[i][k] + dp[k + 1][j] + a[i] * a[k + 1] * a[j + 1]; // 循环找到最佳分割点k
				if (t < dp[i][j])
					dp[i][j] = t;
			}
		}
	}
	cout << dp[0][n - 1] << endl;
	return 0;
}

算法分析与知识点
为了方便理解可将原问题转化为矩阵连乘问题,将矩阵 表示为 。问题即为计算 的最优计算策略。设计这个计算次序在矩阵 和 之间断开先计算 、 的计算量,再将结果相乘可得到最后的结果。

实验题目:能量字符串

题目描述:
两个字符串 A 和 B ,长度都为 n 的,每次从字符串A的两个端头(或左或右)取走一个字符,而B串只能按序从左往右取一个字符。每个字符的ASCII码值为该字符的能量值(区分大小写)。假设第 i 次取走的字符串A字符为 Ax(A串剩余的左端或右端字符),B字符串取第i个字符为Bi,将Ax和Bi字符结合后,其能量值为两个字符能量值的乘积;求所有A串中的字符与B串中的字符结合后,所能获得的最大能量值是多少。
输入要求:
每次输入2行字符串,分别表示字符串A和字符串B;输入保证2个字符串的长度相同。
其中字符串A和B中的字符为大写或小写的英文字符;
A、B字符串的长度都不超过10个字符。

输出要求:
输出2个字符串所能得到的最大能量值。
实验代码

#include <bits/stdc++.h>
using namespace std;
#define N 11
int ans = 0;
int n;
string a, b;
void dfs(int l, int r, int now, int sum) {
	if (now == n) {
		ans = max(ans, sum);
		return;
	}
	dfs(l + 1, r, now + 1, sum + a[l] * b[now]); //递归左子问题
	dfs(l, r - 1, now + 1, sum + a[r] * b[now]); // 递归右子问题
}

int main() {

	cin >> a >> b;
	n = a.length();
	dfs(0, n - 1, 0, 0);
	cout << ans << endl;
	return 0;
}
  • 8
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Andy-wen

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

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

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

打赏作者

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

抵扣说明:

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

余额充值