【算法——动态规划(DFS——记忆化——DP一层更比一层强)】

基础理论

递归:

递:大问题分解子问题的过程  ; 归:产生答案 

dp:只进行归;用已知的最底层的(递归的边界,搜索树的底),推出未知

《视频索引》

一句话:

dp数组(不一定是数组,也可以是有限数间的来回递推)

递推关系:dfs向下递归的公式

dp数组初始化:递归的边界

动态规划题目的基础就是:回溯——记忆化——dp(一层比一层效率高)

例:

例题

打家劫舍

打家劫舍1:LCR 089. 打家劫舍

线性结构,常规做法

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

vector<int>nums = { 10,11,7,12 };

vector<int>memo(100, -1);

int dfs(int index)    //暴力
{
	if (index >= nums.size()) return 0;
	return max( dfs(index + 2) + nums[index], dfs(index + 1) );  //这两个dfs分别代表选和不选,两种情况下的max
}

int mem(int index)   //记忆化
{
	if (memo[index] != -1) return memo[index];   //这个数下面的最大值我们已经记录了,直接返回即可
	if (index >= nums.size()) return 0;
	return memo[index]= max(mem(index + 2) + nums[index], mem(index + 1));  //记录当前下面子树最大值
}

int main()
{
	cout << "暴力:"<< dfs(0) << endl << "记忆化:" << mem(0) << endl;

	vector<int>dp(nums.size(), 0);  //dp.1          dp[i]就是当前房屋的max
	dp[0] = nums[0]; dp[1] = max(nums[0],nums[1]);
	for (int i = 2; i < nums.size(); i++)
		dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);  //画图,一定要画图,用: 10 11 7 12等试试看,会很通透


	int a = nums[0], b = max(nums[0], nums[1]), c = 0;    //dp.2
	for (int i = 2; i < nums.size(); i++)    
	{
		c = max(a + nums[i], b);
		a = b;
		b = c;
	}

	cout << "dp1:" << dp[nums.size() - 1] << endl;

	cout << "dp2:" << b << endl;

	return 0;
}

打家劫舍2:LCR 090. 打家劫舍 II

环形结构,转换为线性

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

//针对打家劫舍2的核心思想就是:1不考虑首元素,2不考虑尾元素,3首尾都不考虑 这三种情况;把环形问题转换为线性问题
//改进:3可以排除,因为1/2以及包含3了

//dfs  dfs  dfs
vector<int>nums = { 2,7,9,3,1 };
int dfs1(int index)   //最后一家不选
{
	if (index >= nums.size() - 1) return 0;
	return max(dfs1(index + 2) + nums[index], dfs1(index + 1));
}

int dfs2(int index)  //第一家不选
{
	if (index >= nums.size()) return 0;
	return max(dfs2(index + 2) + nums[index], dfs2(index + 1));
}

int dfs3(int index)  //首尾都不选    这个可以忽略
{
	if (index >= nums.size() - 1) return 0;
	return max(dfs3(index + 2) + nums[index], dfs3(index + 1));

}




int dp_function(int index,int size,vector<int>nums)   //dp封装起来
{
	vector<int>dp(size);
	dp[index] = nums[index];
	dp[index + 1] = max(nums[index], nums[index + 1]);
	for (int i = index + 2; i < size; i++)
		dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
	return dp[size - 1];
}

int main()
{
	//这个要有
	if (nums.size() == 1) return nums[0];
	if (nums.size() == 2) return max(nums[0], nums[1]);

	//dfs
	cout << max(dfs1(0), max(dfs2(1), dfs3(1))) << endl;

	//dp
	cout << max(dp_function(0, nums.size() - 1,nums), dp_function(1, nums.size(),nums));



	return 0;
}

打家劫舍3:​​​​​​337. 打家劫舍 III

树形结构

爬楼梯(斐波那契)

递推草稿&视频:动态规划(dp)入门 | 这tm才是入门动态规划的正确方式! | dfs记忆化搜索 | 全体起立!!_哔哩哔哩_bilibili

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

vector<int>memo(100, -1);
int mem(int index)      //记忆化(剪枝)
{
	if (memo[index] != -1) return memo[index]; 
	if (index == 1) return 1;
	if (index == 2) return 2;
	memo[index] = mem(index - 1) + mem(index - 2);

	return memo[index];
}

int dfs(int index)  //暴力
{
	if (index == 1) return 1;
	if (index == 2) return 2;

	return dfs(index - 1) + dfs(index - 2);
}


int time(int(*k)(int),int n)  //用时测试函数
{
	auto start = std::chrono::steady_clock::now();
	k(n);
	auto end = std::chrono::steady_clock::now();
	return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
}

int main()
{
	int n; cin >> n;

	cout << dfs(n) << endl << mem(n) << endl;

	memo.clear();  memo.resize(100,-1);

	cout <<"暴力用时:"<< time(dfs, n) << "   " <<"记忆化用时:" << time(mem, n) << endl;

	vector<int>dp(n, 0);  //dp   当然也可以用三个变量来互相推
	dp[0] = 1, dp[1] = 2;
	for (int i = 2; i < n; i++)
		dp[i] = dp[i - 1] + dp[i - 2];
	cout << dp[n - 1] << endl;

	return 0;
}

数字三角形

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

//最大子路径:max    最小min

vector<vector<int>>nums(100, vector<int>(100, 0));
int n;
vector<vector<int>>memo(100, vector<int>(100, -1));  

int dfs(int x, int y)  //暴力
{
	if (x >=n || y >= n ) return 0;
	return max(dfs(x + 1, y) + nums[x][y], dfs(x + 1, y + 1) + nums[x][y]);
}

int mem(int x,int y)   //记忆化(剪枝)
{
	if (memo[x][y]!=-1) return memo[x][y];
	if (x >= n || y >= n) return 0;
	return memo[x][y] = max(mem(x + 1, y) + nums[x][y], mem(x + 1, y + 1) + nums[x][y]);
}

int main()
{
	cin >> n;
	for (int i = 0; i < n; i++)
		for (int j = 0; j <= i; j++)
			cin >> nums[i][j];
	cout << "暴力:" << dfs(0, 0) << endl;

	cout << "记忆化:" << mem(0, 0) << endl;

	vector<vector<int>>dp(n + 1, vector<int>(n + 1, 0));  //注意这个+1;自己思考一下
	for (int i = n - 1; i >= 0; i--)   //dp  从下往上
		for (int j = 0; j < n; j++)
            dp[i][j]=max(dp[i+1][j+1]+nums[i][j],dp[i+1][j]+nums[i][j]);

	cout << "dp(从下往上递推也就是遵循从上往下递归):" << dp[0][0] << endl;
	return 0;
}

数字三角形递推2(上往下也就是遵循下往上递归)

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

int main()
{
    //1  从1开始
	int n; cin >> n;
	vector<vector<int>>nums(n + 1, vector<int>(n + 1, 0));
	//注意:建议以后动态规划题,原始数据的存储从1开始而不是0(方便),后面有从0开始
	for (int i = 1; i <= n; i++)   //注意取等
		for (int j = 1; j <= i; j++)   //注意取等
			cin >> nums[i][j];
	cout << endl;

	vector<vector<int>>dp(n + 1, vector<int>(n + 1, 0));

	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			dp[i][j] = max(dp[i - 1][j] + nums[i][j], dp[i - 1][j - 1] + nums[i][j]);

	for (auto i : dp)   //强烈建议打印dp数组,观察数据是否正常
	{
		for (auto j : i)
			cout << j << " ";
		cout << endl;
	}

	//cout << dp[n][n] << endl;  //err   如果直接到这里就结束就错了

	//这一步是干嘛?:我们从下到上dp最后答案就是dp[0][0],但是现在上到下,所以最后一整行都是和"下到上的dp[0][0]"同性质的,所以我们需要求max
    //还有一种方法,就是在上面dp时就应用res
	int res = 0;
	for (int i = 1; i <= n; i++)
		res = max(res, dp[n][i]);

	cout << res << endl;





	//2  从0开始存储
	vector<vector<int>>nums2(100, vector<int>(100, 0));
	cin >> n;
	for (int i = 0; i < n; i++)
		for (int j = 0; j <= i; j++)
			cin >> nums2[i][j];
	cout << endl;

	vector<vector<int>>dp2(n + 1, vector<int>(n + 1, 0));
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			dp2[i][j] = max(dp2[i - 1][j] + nums2[i - 1][j - 1], dp2[i - 1][j - 1] + nums2[i - 1][j - 1]);

	for (auto i : dp2)
	{
		for (auto j : i)
			cout << j << " ";
		cout << endl;
	}

	res = 0;
	for (int i = 1; i <= n; i++)
		res = max(res, dp2[n][i]);

	cout << res << endl;


	return 0;
}

最小路径

64. 最小路径和 - 力扣(LeetCode)

和上一个数字三角形差不多

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

int main()
{
	int x, y; cin >> x >> y;
	vector<vector<int>>nums(x, vector<int>(y, 0));
	for (int i = 0; i < x; i++)
		for (int j = 0; j < y; j++)
			cin >> nums[i][j];

	vector<vector<int>>dp(x + 1, vector<int>(y + 1, 0));

	for (int i = x - 1; i >= 0; i--)
		for (int j = y - 1; j >= 0; j--)
		{
			if (i + 1 >= x && j + 1 >= y) dp[i][j] = nums[i][j];
			else if (i + 1 >= x) dp[i][j] = dp[i][j + 1] + nums[i][j];
			else if (j + 1 >= y) dp[i][j] = nums[i][j] + dp[i + 1][j];
			else dp[i][j] = max(dp[i + 1][j] + nums[i][j], dp[i][j + 1] + nums[i][j]);
		}

	cout << dp[0][0];

	return 0;
}

买卖股票问题

买卖股票1:121. 买卖股票的最佳时机

暴力dfs:会超时
#include<iostream>
#include<vector>
using namespace std;
int sum = 0;
int dfs(vector<int>nums, int index)
{
	if (!index) return 0;  //结束条件
	for (int i = index-1; i >= 0; i--)   //深度
		sum = max(sum, nums[index] - nums[i]);   //当前抛股最大值。
	sum=max(sum,dfs(nums, index - 1));  //宽度 
	return sum;
}

int main()
{
	vector<int>nums = { 7,1,5,3,6,4 };
	int n = nums.size();
	cout << dfs(nums, n - 1);

	return 0;
}
dp1
#include<iostream>
#include<vector>
using namespace std;
//求:花的钱少&回报高
int main()
{
	vector<int>nums = { 7,1,5,3,6,4 };
	int n = nums.size();
	vector<vector<int>>dp(n, vector<int>(2));
	dp[0][0] = -nums[0];  //买入  当前状态
	dp[0][1] = 0;         //卖出回报
	for (int i = 1; i < n; i++)
	{
		dp[i][0] = max(dp[i - 1][0],0 - nums[i]);  //入股,之前买入和现在买入(投资)  注意这个0一点也不多余,等到第二题你就明白了。
		dp[i][1] = max(dp[i - 1][1], nums[i] + dp[i][0]);   //捞钱:之前捞和现在捞(卖出+买入=利润;)
	}
	cout << dp[n - 1][1];


	return 0;
}
贪心
#include<iostream>
#include<vector>
using namespace std;
int main()
{
	vector<int>nums = { 7,1,5,3,6,4 };
	int i = 1e9, j = 0;
	for (auto n : nums)
	{
		j = max(j, n - i);
		i = min(i, n);
	}
	cout << j << endl;
	return 0;
}

买卖股票2:122. 买卖股票的最佳时机 II

dp1
#include<iostream>
#include<vector>
using namespace std;
//求:花的钱少&回报高
int main()
{
	vector<int>nums = { 7,1,5,3,6,4 };
	int n = nums.size();
	vector<vector<int>>dp(n, vector<int>(2));
	dp[0][0] = -nums[0];  //买入  当前状态
	dp[0][1] = 0;         //卖出回报
	for (int i = 1; i < n; i++)
	{
		dp[i][0] = max(dp[i - 1][0],dp[i-1][1] - nums[i]);  //入股,之前买入和现在买入(投资,上一次赚的钱-这一次投入)
		dp[i][1] = max(dp[i - 1][1], nums[i] + dp[i][0]);   //捞钱:之前捞和现在捞(卖出+买入=利润;)
	}

	cout << dp[n - 1][1];

	return 0;
}

买卖股票3:123. 买卖股票的最佳时机 III

dp1
#include<iostream>
#include<vector>
using namespace std;
//求:花的钱少&回报高
int main()
{
	vector<int>nums = {3,3,5,0,0,3,1,4};
	int n = nums.size();
	vector<vector<int>>dp(n, vector<int>(5, 0));
	dp[0][0] = 100;                   //原始资金随便你多少

	dp[0][1] = dp[0][0] - nums[0];  //第一次买入
	dp[0][2] = dp[0][1] + nums[0];  //第一次卖出   0

	dp[0][3] = dp[0][2] - nums[0];  //第二次买入   
	dp[0][4] = dp[0][3] + nums[0];  //第二次卖出   0

	for (int i = 1; i < n; i++)
	{
		dp[i][0] = dp[i - 1][0];
		dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - nums[i]);  //第一次买
		dp[i][2] = max(dp[i - 1][2], dp[i - 1][1] + nums[i]);  //第一次卖

		dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - nums[i]);  //第二次买
		dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + nums[i]);  //第二次卖
	}
	cout << dp[n - 1][4] - 100;   //减去原始资金=净利润

	return 0;
}

买卖股票4:188. 买卖股票的最佳时机 IV

dp1
#include<iostream>
#include<vector>
using namespace std;
int main()
{
	vector<int>nums = { 8,6,4,3,3,2,3,5,8,3,8,2,6 };

	int k = 2; //买卖几次

	int n = nums.size();
	vector<vector<int>>dp(n, vector<int>(2*k+1,0));
	dp[0][0] = 0;   //原始资金

	for (int i = 1; i < 2*k+1; i++)              //dp数组初始化
		if (i % 2) dp[0][i] = dp[0][i - 1] - nums[0];   //买
		else dp[0][i] = dp[0][i - 1] + nums[0];         //卖

	for (int i = 1; i < n; i++)
	{
		int x = nums[i];
		for (int j = 1; j < 2*k+1; j++)
		{
			x *= -1;
			dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] + x);
		}
	}

	cout << dp[n - 1][2*k];

	//for (auto i : dp)
	//{
	//	for (auto j : i)
	//		cout << j << " ";
	//	cout << endl;
	//}

	return 0;
}

买卖股票5:309. 买卖股票的最佳时机含冷冻期

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

int main()
{
	vector<int>nums = { 1,2,3,0,2 };
	int n = nums.size();
	vector<vector<int>>dp(n, vector<int>(4, 0));
	//4个状态都是最大利润(口袋里的钱)
	dp[0][0] = -nums[0];  //买入
	dp[0][1] = 0;         //保持卖出状态    口袋钱状态
	dp[0][2] = 0;         //今天卖   捞钱
	dp[0][3] = 0;         //冷冻期
	for (int i = 1; i < n; i++)
	{
		//保持前一天买入状态   今天买:前一天是冷冻期   前一天不是冷冻期,前一天卖出状态
		dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3] - nums[i], dp[i - 1][1] - nums[i]));  
		dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);   //保持前一天卖出状态    ****前一天是冷冻期:不能是dp[i-1][3]+nums[i]你还没有买呢,不能加
		dp[i][2] = dp[i - 1][0] + nums[i];   //当天卖
		dp[i][3] = dp[i - 1][2];    //今天是冷冻期,说明昨天卖了股票,所以利润就是昨天卖的
	}
	cout << max(dp[n - 1][1], max(dp[n - 1][2], dp[n - 1][3]));

	return 0;
}

背包问题:

01背包:2. 01背包问题 - AcWing题库

dfs&记忆化:  dfs当数据量大就会超时/爆栈(有大量重复)

这里有两种dfs方法,两种思想,有注释,自己分析

//                ***************《dfs1》****************
#if 0
#include<iostream>
#include<vector>
using namespace std;

vector<int>S;   //记录每一个
int sum_2 = 0;  //答案
//v&s:每个物品体积&价值   index:索引,操作哪个物品   V:当累积包体积   sum:当前价值累加
void dfs(vector<int>v,vector<int>s, int index,int V,int sum)
{
	if (index > v.size() || V < 0) return;  //1这里index不需要设置为>=0 不然会有元素读不到(2)
	if (V >= 0)      //2
	{
		S.push_back(sum);
		sum_2 = max(sum_2, sum);
	}

	for (int i = index; i < v.size(); i++)   //1哪怕把v.size()放进来,这里也会有一个for约束
		dfs(v, s, i + 1, V - v[i], sum + s[i]);

}


int main()
{
	int N, V;
	cin >> N >> V;    //物品数量和背包总体积  4  5
	vector<int>v(N);  //每个物品的体积       1 2 3 4
	vector<int>s(N);  //每个物品的价值       2 4 4 5
	for (int i = 0; i < N; i++)
		cin >> v[i] >> s[i];

	dfs(v, s, 0, V, 0);
	for (auto i : S)
		cout << i << " ";
	cout << endl;
	cout << sum_2;

	return 0;
}   


#else      //************《dfs2 指数型枚举(选和不选方法)》***************
#include<iostream>
#include<vector>
using namespace std;
int N, V;
vector<int>v;
vector<int>s;

//V:剩余容量   index:当前索引到的物品

int dfs(int V,int index)    //dfs暴力 
{
	if (index >= N)
		return 0;
	else if (V - v[index] < 0)   //当前背包容量装不下这个物品   和指数型枚举的区别也正是这里,加了一个容量判断
		return dfs(V, index + 1);      //所以跳过当前物品
	else if (V - v[index] >= 0)     //当前背包装的下这物品
		return max(dfs(V - v[index], index + 1) + s[index], dfs(V, index + 1));   //选择这个物品和不选择当前物品
}


vector<int>memo;
int m(int V, int index)   //M 记忆化
{
	if (memo[V])  
		return memo[V];     //这里要以体积为记忆化对象,代表着当前容量的最大价值
	if (index >= N) 
		return memo[V] = 0;
	else if (V - v[index] < 0) 
		return memo[V] = dfs(V, index + 1);
	else if (V - v[index] > 0) 
		return memo[V] = max(dfs(V - v[index], index + 1) + s[index], dfs(V, index + 1));

}

int main()
{
	cin >> N >> V;
	v.resize(N);
	s.resize(N);
	for (int i = 0; i < N; i++)
		cin >> v[i] >> s[i];

	cout << dfs(V, 0) << endl;   //dfs

	memo.resize(V+1);     //这里分配空间要+1,因为V这个容量就是我们的答案
	cout << m(V, 0) << endl;      //M

	return 0;
}



#endif
dp:
#include<iostream>
#include<vector>
using namespace std;

int  main()
{
	int N, V;
	cin >> N >> V;
	vector<int>v(N);
	vector<int>s(N);
	for (int i = 0; i < N; i++)
		cin >> v[i] >> s[i];

	//********dp1二维动态规划********观察可以得出,每一个dp依赖于上方dp&上一行左边某一个dp;
    //for顺序可调换,j也可以倒序。注意倒序查看时我们的01背包理解图也要倒过来。
	vector<vector<int>>dp(N+1, vector<int>(V + 1));   //注意V和N都要+1
	//dp[i][j]:0-i物品中自由选(一个选一次)并且容量不超过j的最大价值

    //问题1:为什么N+1,V+1?不加行不行
	for (int i = 1; i <= N; i++)
	{
		for (int j = 0; j <= V; j++)  
		{
            //问题1:因为我们这里i是1开始,如果我们N不取等,就会导致最后一个物品无法加入计算
			//自然的j可以取到V是因为我们要考虑容量为V时的情况
			if (j - v[i-1] >= 0)    //j-v[i]当前容量-当前物品容量
				//dp[i-1][j]为不选i物品,所以保持    dp[i - 1][j - v[i-1]] + s[i-1]为选择i物品
				dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i-1]] + s[i]);  //从这个表达式也可以看出,每一个dp依赖上方格子和上方左边某一个格子;
			else
				dp[i][j] = dp[i - 1][j];
		}
	}
	for (auto i : dp)
	{
		for (auto j : i)
			cout << j << " ";
		cout << endl;
	}

	cout << endl;
	//************dp2空间压缩一维动态规划************
    //j只能倒序,for循环不可调换
    //上一层拷贝到当前层,然后当前层引入了新的物品,再进行比较这个新物品选还是不选
	vector<int>dp2(V + 1);
	for (int i = 0; i < N; i++)
	{
//这个j循环为什么倒序:反正重复,自己正序遍历推导一下。
		for (int j = V; j >= v[i]; j--)  
		{
			dp2[j] = max(dp2[j], dp2[j - v[i]] + s[i]);
			cout << dp2[j] << " ";
		}
		cout << endl;
	}
		

	cout << endl;
	for (auto i : dp2)
		cout << i << " ";


	return 0;
}

错误dp思想:

    /*    错误dp
	vector<vector<int>>dp(N+1, vector<int>(2));  //0:价值    1:容量
	dp[0][0] = 0;
	dp[0][1] = V;
	for (int i = 1; i <= N; i++)
	{
		if (dp[i-1][1] - v[i-1] >= 0)
		{
			dp[i][0] = max(dp[i-1][0] + s[i-1], dp[i-1][0]);
			if (dp[i][0] == dp[i-1][0]) 
				dp[i][1] = dp[i-1][1];
			else 
				dp[i][1] = dp[i-1][1] - v[i-1];
		}
		else   //装不下  保持现状
		{
			dp[i][0] = dp[i - 1][0];
			dp[i][1] = dp[i - 1][1];
		}
	}
    */ 

完全背包:3. 完全背包问题 - AcWing题库

物品可以重复选了

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

int main()
{
	int N, V;
	cin >> N >> V;
	vector<int>v(N);
	vector<int>s(N);
	for (int i = 0; i < N; i++)
		cin >> v[i] >> s[i];
	vector<vector<int>>dp1(N + 1, vector<int>(V + 1));
	for (int i = 1; i <= N; i++)
		for (int j = 0; j <= V; j++)
		{
			if (j - v[i - 1] >= 0)
				dp1[i][j] = max(dp1[i - 1][j - v[i - 1]] + s[i - 1], dp1[i - 1][j]);
			else
				dp1[i][j] = dp1[i - 1][j];
		}

	for (auto i : dp1)
	{
		for (auto j : i)
			cout << j << " ";
		cout << endl;
	}

	cout << endl;

	vector<int>dp2(V + 1);
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j <=V; j++)
		{
			if (j - v[i] >= 0)
				dp2[j] = max(dp2[j - v[i]] + s[i], dp2[j]);
			//cout << dp2[j] << " ";      这个可以不要就是拿来观察的
		}
		cout << endl;
	}

	for (auto i : dp2)
		cout << i << " ";

	return 0;
}

多重背包:4. 多重背包问题 I - AcWing题库

01背包思想解决

#include<iostream>
#include<vector>
using namespace std;
int N, V;
vector<int>v;  //单个体积
vector<int>w;  //单个价值
vector<int>s;  //数量

int main()
{
	cin >> N >> V;
	v.resize(N);
	w.resize(N);
	s.resize(N);

	for (int i = 0; i < N; i++)
		cin >> v[i] >> w[i] >> s[i];

	vector<int>dp(V + 1);
		for (int i = 0; i < N; i++)
			for (int k = 0; k < s[i]; k++)
				for (int j = V; j >= 0; j--)   //看这个就可以区别01和完全
					if (j - v[i] >= 0) dp[j] = max(dp[j], dp[j - v[i]] + w[i]);

		cout << dp[V];


	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值