三星研究院(南京)机试练习(深度优先搜索DFS && 广度优先搜索BFS经典题目及模板总结)

搜索小贴士: 

DFS、BFS其实是图的一种遍历方式,两种算法均可以遍历整张图,是非常暴力的算法,能解决大多数对时间要求不高的问题,但其应用场景却不太一样,由于他们两种的算法特性导致的,如下:

1.问最短、最少之类的问题,一般要想到BFS,BFS搜索是逐层搜索,达到本层时所花费的value值是一样的,由于BFS借助队列实现,而队列有先进先出的特性,所以当找到结果时,是从上一层继承下来的,一定是当前问题的最少花费。求最短路问题时,优先用bfs,因为dfs需要遍历所有情况(此处思考一下为什么)才能找到最短路(spfa算法正是借助bfs来实现的)

2.DFS一般会伴随着回溯,只需要记得DFS是一条路走到黑,走不通时返回到最近一个已访问过的且有其他岔路的节点(至少有一条岔路必须还未访问过),然后访问该岔路

3.DFS是一种可以“后悔”的算法,通常都是使用回溯来后悔的

那么,为什么DFS需要遍历所有情况才能找到最短路?因为他每一次都是一条路走到黑,在没有遍历全部节点时根本不知道那一条路是最近的

DFS模板:

void dfs(当前位置,节点列表){

    if(满足结束条件){
        记录结果;
        return;
    }
    for(选择:节点列表){
        处理当前节点;
        dfs(当前位置,节点列表);
        撤销选择;//回溯的精髓
    }
}

BFS模板:

int vis[x];//标记数组,用来标记该节点是否曾经入队,避免节点重复入队导致死循环
queue<int>q;//或可以用数组辅助实现队列,int q[100],int pre=0,rear=0;队头和队尾
while(!q.empty()){
	int now=q.front();//取队首元素
	q.pop();//弹出队首元素
	步骤1.找出当前节点走一步可以接触到的地方即邻接点
	步骤2.在vis数组中查询步骤1找到的节点
    步骤3.将未插入vis数组的节点入队
}

POJ - 1321

7d8536f2d1204d83ac6285488bbc9bf0.png

思路:n皇后变形题,区别在于不需要每行都要放上棋子,所以每一行我们可以选择放or不放

不需要去判断每一行是否有其他棋子放置,因为我们是一行放一个,放的时候用istrue去判断当前位置是否合法,再设置一个标记数组记录当前位置,递归返回后记得取消标记

#include<iostream>
using namespace std;
char mp[10][10];
int vis[10][10];
int ans = 0;
bool istrue(int x,int y,int n){
	//同一列
	for (int i = x-1; i > -1; --i){
		if (vis[i][y])return 0;
	}
	return 1;
}
void dfs(int now,int n,int k){//now是当前行号,n*n棋盘,k个棋子
	if (k == 0){//只要放完棋子就可以记录
		ans++;
		return;
	}
	if(now>=n)return;
	for (int i = 0; i < n; ++i){
		if (mp[now][i]=='#'&&istrue(now, i,n)){
			vis[now][i] = 1;//now是当前行号
			dfs(now + 1, n, k - 1);//放下
			vis[now][i] = 0;//回溯
		}
	}
	dfs(now+1,n,k);//不放
}
int main(){

	int n=0, m=0;
	while (cin >> n >> m){
		if (n == -1 && m == -1)break;
		for (int i = 0; i < n; ++i){
			for (int j = 0; j < n; ++j){
				cin >> mp[i][j];
			}
		}
		dfs(0,n,m);
		cout<<ans<<endl;
		ans=0;
	}
	return 0;
}

POJ - 2251s

题意:一个三维牢笼,求从S开始寻到E的最短路径,该题输入数据时不用读取空格。

思路:迷宫问题变形,难点在于理解题目意思,对于三维牢笼的建图,我们可以采用三维数组,三维数组a[z][x][y]可以看成z个二维数组的集合(z即可以看成z层牢笼),对于二维迷宫问题,是处于平面上的,移动时只有四个方向,上下左右,而三维迷宫问题则可以移动6个方向,如下图,z坐标轴上的移动可以看成从某一层移动到其他层,其他坐标轴则是我们常见的二维迷宫问题了,在二维迷宫问题上面多了一个Z坐标轴。

8107a9e7b6dd7bc3d60c6165ef0a8c46.png

定义结构体node,存储位置和当前步数

#include<iostream>
#include<stdio.h>
#include<malloc.h>
#include<queue>
#include<string>
using namespace std;
struct node{
	int z,x, y;
	int step;
};
char mp[40][41][41];
int vis[41][41][41];
int mv[6][3] = { { -1, 0, 0 }, { 1, 0, 0 }, { 0, -1, 0 }, { 0, 1, 0 }, { 0, 0, -1 }, { 0, 0, 1 } };
//int vis[40];
int main(){

	int n = 0, m = 0, o = 0;

	while (cin >> n >> m >> o , m+n+o){
		int sz, sx, sy,ez,ex,ey;
		for (int i = 0; i < n; ++i){
			
			for (int j = 0; j < m; ++j){
				for (int k = 0; k < o; ++k){
					cin >> mp[i][j][k];
					if (mp[i][j][k] == 'S'){
						sz = i, sx = j, sy = k;
					}
					/*else if (mp[i][j][k] == 'E'){
						ez = i, ex = j, ey = k;
					}*/
				}
			}
			//string temp; cin >> temp;
		}
		
		//BFS
		int flag = 0;
		memset(vis, 0, sizeof(vis));
		queue<node>q;
		node now;
		now.z = sz, now.x = sx, now.y = sy, now.step = 0;
		vis[sz][sx][sy] = 1;
		q.push(now);
		while (!q.empty()){
			now = q.front(); q.pop();
			if (mp[now.z][now.x][now.y] == 'E'){
				cout << "Escaped in " << now.step <<" minute(s)."<<endl ;
				flag = 1;
			}
			for (int i = 0; i < 6; ++i){
				node temp = now;
				int z = now.z + mv[i][0], x = now.x + mv[i][1],y = now.y + mv[i][2] ;
				
				if (z>-1 && z<n&&x>-1 && x<m&&y>-1 && y<o&&vis[z][x][y] == 0 && (mp[z][x][y] == '.' || mp[z][x][y] == 'E')){
					now.z = z, now.x = x, now.y = y,now.step=now.step+1;
					q.push(now);
					now = temp;
					vis[z][x][y] = 1;
				}
			}
		}
		if (!flag)cout << "Trapped!" << endl;
		//q.
		//BFS
		/*int flag = 0;
		char *q = new char[40]();
		//int *vis = new int[40](),*step=new int[40]();
		int rear = 0,pre=0;
		q[rear++] = mp[0][0][0];
		while (pre != rear){
			
		}*/
	}
	return 0;
}

Catch That Cow

 POJ - 3278 

题目大意:输入两个数n,m,求n到m的最短距离(n只有三种走法,前后移动一个位置或移动到2*n的位置)

思路:无脑搜索即可,只需要注意1.不能移动到小于0的位置;2.必须小于等于abs(n-m),此处的n是初始n,因为abs(n-m)就是一格一格移动到终点,没有花里胡哨的步骤;3.不可移动至(max(n,m)+1)*2的位置

BFS:

#include<iostream>

#include<queue>
#include<string>
#include<map>
using namespace std;
int ans;
map<int, int>dp;
int n;
struct node{
	int pos, step;
};

int abs(int x, int y){
	if (x < y)return y-x;
	return x-y;
}
int max(int x, int y){
	if (x>y)return x;
	return y;
}
int main(){

	int  m ,maxn;
	//while (cin >> n >> m){
		cin >> n >> m;
		maxn = max(m,n)+1;
		int flag = 0;
		
		node *q = new node[400000 + 10]();
		int *vis = new int[400000 + 10]();
		int pre = 0, rear = 1;
		q[0].pos = n, q[0].step = 0;
		vis[n] = 1;
		while (pre != rear){

			node temp = q[pre++];
			if (temp.pos == m){
				cout<< temp.step<<endl;
				break;
			}
			for (int i = 0; i < 3; ++i){
				node now = temp;
				if (i == 0){
					now.pos += 1;
				}
				else if (i == 1){
					now.pos -= 1;
				}
				else{
					now.pos *= 2;
				}
				now.step++;
				//if (now.step > abs(n - m) || now.pos<0 || now.pos>maxn || vis[now.pos])continue;
				if (now.pos >= 0 && !vis[now.pos]&&now.step<=abs(n-m)&&now.pos<=maxn*2){
					vis[now.pos] = 1;
					q[rear++] = now;
					
				}
			}
		} 
		
	//}
	
	return 0;
}

poj 3009 

Curling 2.0

#include<iostream>

#include<algorithm>
#include<stdio.h>
#include<vector>
int w,h,sx,sy,ex,ey;
int maze[25][25],flag,ans;
int mv[4][2] = { {-1,0}, {0,-1}, {1,0}, {0,1} };//上左,下,右
int min(int x, int y){
	return x < y ? x : y;
}
void dfs(int des,int cnt,int x,int y){//des表示方向
	//if (flag)return;
	
	
	if (x == ex&&y == ey){
		flag = 1;
		ans = min(cnt, ans);
		return;
	}if (cnt >= 10)return;
	for (int i = 0; i < 4; ++i){
		//if (flag)return;
		int nx = x + mv[i][0],ny=y+mv[i][1];
		if (nx < 0 || nx >= h || ny < 0 || ny >= w)continue;
		//if (x == sx&&y == sy){
		if (maze[nx][ny] == 1)continue;//紧邻墙壁无法移动;
		//}
		//if (i == 0){
		int yuejie = 0;
		while (maze[nx][ny] == 0||maze[nx][ny]==2){//继续往该方向走下去,只要不越界
			nx += mv[i][0], ny += mv[i][1];
			if (nx < 0 || nx >= h || ny < 0 && ny >= w){
				//nx -= mv[i][0], ny -= mv[i][1];//越界了  退回去
				yuejie = 1;
				break;
			}
			if (x == ex&&y == ey){
				flag = 1;
				ans = min(cnt, ans);
				//return;
			}
		}if (yuejie)continue;//越界了说明走不通
		
		int pos = 0;
		if (maze[nx][ny] == 1){
			maze[nx][ny] = 0;
			pos = 1;
			//dfs(i, cnt + 1, nx, ny);
			dfs(i, cnt + 1, nx - mv[i][0], ny - mv[i][1]);
		}
		else{
			dfs(i, cnt + 1, nx, ny );
		}
		
		if (pos)maze[nx][ny] = 1;
		
		
	}
}
using namespace std;
int main(){
	int cnt = 0;
	vector<int>temp;
	while (cin >> w >> h,w&&h){
		for (int i = 0; i < 25; ++i){
			for (int j = 0; j < 25; ++j)maze[i][j] = 0;
		}
		flag = 0;
		ans = 0x3f3f3f3f3f;
		for (int i = 0; i < h; ++i){
			for(int j = 0; j < w; ++j){
				cin >> maze[i][j];
				if (maze[i][j] == 2){
					sx = i, sy = j;
		//			maze[i][j] = 0;
				}
				else if (maze[i][j] == 3){
					ex = i, ey = j;
				}
			}
		}
		//if ()
		
		dfs(0,0,sx,sy);
		//cout << "结果"<<(++cnt)<<":";
		/*if (flag)temp.push_back(ans);
		else temp.push_back(-1);*/
		if (flag)
		cout << ans << endl; 
		else cout << -1 << endl;
		
	}
	//for (auto x : temp)cout << x << " ";
	return 0;
}

poj 1426

Find The Multiple

题意:给定一个数字n,求n的倍数(只能由0和1组成)

思路:直接暴搜两种情况,从1开始求倍数,*10或*10+1,每个样例的最小值不会超过long long的范围,所以*18次后还没结果就可以返回咯

BFS的做法更简单,不需要判定超过longlong的次数,直接搜索就行,但是队列手动模拟的话就做不出来了

DFS:

#include<iostream>

#include<algorithm>
#include<stdio.h>
#include<vector>
using namespace std;
long long n;
long long ans;
int flag;
void dfs(long long now,int cnt){
	if (flag)return;
	if (now%n == 0){
		flag = 1;
		ans = now;
		return;
	}
	if (cnt == 18)return;
	dfs(now*10,cnt+1);
	dfs(now * 10 + 1,cnt+1);
}
int main(){
	while (cin >> n,n){
		flag = 0; ans = 0;
		dfs(1,0);
		cout << ans << endl;
	}
	return 0;
}

BFS:

#include<iostream>
#include<queue>
using namespace std;
long long n;
struct node{
	int cnt;
	long long now;
};

int main(){
	while (cin >> n,n){
		queue<node>q;
		node temp;
		temp.now = 1, temp.cnt = 0;
		q.push(temp);
		while (!q.empty()){
			temp = q.front(); q.pop();
			if (temp.now%n == 0){
				cout << temp.now << endl;
				break;
			}
			node inq;
			inq.now = temp.now * 10, inq.cnt = temp.cnt + 1;
			q.push(inq);
			inq.now++;
			q.push(inq);
		}
	}

	return 0;
}

poj2488 

A Knight's Journey

题意:给定一个棋盘,一个棋子,棋子只能按8个方向(下图中白色圆点就是马棋子从该位置开始可以走到的位置)移动,求该棋子是否能完整走完这个棋盘,若能,输出移动的完整路径。

ps:起点可以任意

思路:有些题解遍历了棋盘所有点作为起点去移动,虽然较为严谨但没有必要,直接从棋盘左上角移动就可以了,因为反推法:既然可以遍历到所有点,那么从任何位置开始都可以遍历完这一整张图

坑点:用string类型保存路径会TLE,必须用char数组,乌鱼子.....

#include<iostream>

#include<algorithm>
#include<queue>
#include<string>
#include<map>
using namespace std;
struct node{
	int x, y,step;
	//string s;
	char s[100];
};
string ans;
int n, m, flag;
int vis[100][100];
int i;
int mv[8][2] = { { -1, -2 }, { 1, -2 }, { -2, -1 }, { 2, -1 }, { -2, 1 }, { 2, 1 }, { -1, 2 }, { 1, 2 } };  //必须
void dfs(int x, int y, node now){
	if (flag)return;
	if (now.step == n*m * 2){
		flag = 1;
		//ans = now.s;
		cout << "Scenario #" << i << ":" << endl;
		for (int i = 0; i < now.step; ++i){
			if (now.s[i] != '\0')
			cout << now.s[i];
		}
		cout << endl << endl;
		//cout << now.s << endl << endl;
		return;
	}
	for (int j = 0; j < 8 && !flag; ++j){

		//node temp;
		int temp_x = x + mv[j][0];
		int temp_y = y + mv[j][1];


		if (flag == 0 && vis[temp_x][temp_y] == 0 && temp_x>0 && temp_x <= n&&temp_y > 0 && temp_y <= m){
			vis[temp_x][temp_y] = 1;
			node temp = now;
			temp.s[temp.step++] = (temp_y - 1 + 'A');
			temp .s[temp.step++]= (temp_x + '0');
			dfs(temp_x, temp_y,temp );
			vis[temp_x][temp_y] = 0;
		}
	}
}
int main(){
	int sum;
	//int n = 0, m = 0;
	cin >> sum;
	for (i = 1; i <= sum; ++i){
		cin >> n >> m;
		for (int k = 0; k <= n; ++k){
			for (int j = 0; j <= m; ++j)vis[k][j] = 0;
		}
		flag = 0;
		//node temp; temp.x = 1, temp.y = 1;
		vis[1][1] = 1;
		node temp;
		temp.s[0] = 'A', temp.s[1] = '1'; temp.step = 2;
		dfs(1, 1, temp);
		if (!flag){
			cout << "Scenario #" << i << ":" << endl;
			cout << "impossible" << endl<<endl;
		}
	}

	return 0;
}

3126 -- Prime Path

素数打表+BFS

题意:给两个数A,B,求A到B需要变化的最小次数,每次变化时都必须是素数

思路:先把10000内的素数打表,然后分个、十、百、千位去搜索,除千位以外都可0-9,千位不可取0

还是细节问题。注意数组别开的太小,给定的数>=1000,不能包含前导0。

#include<iostream>
using namespace std;
int T;
int n, m;
int prime[50000],vis[50000];
struct node{

	int num,cnt;
};
int POW(int x, int y){
	if (y == 0)return 1;
	for (int i = 2; i <= y; ++i){
		x *= 10;
	}
	return x;
}
int ans = 0;
node q[20000];
void bfs(){

	//node *q = new node[20000]();
	int pre = 0, rear = 1;
	q[0].num = n, q[0].cnt = 0;
	vis[n] = 1;
	while (pre != rear){
		node now = q[pre++];
		if (now.num == m){
			cout << now.cnt << endl;
			ans = 1;
			break;
		}
		int temp;
		temp = now.num / 10 * 10;//个位
		for (int i = 0; i < 10; ++i){
			int temp2 = temp + i;
			//temp += i;
			if (vis[temp2] == 0 && prime[temp2]){
				node p; p.num = temp2, p.cnt = now.cnt + 1;
				q[rear++] = p;
				vis[temp2] = 1;
			}
		}
		temp = now.num / 100 * 100+now.num%10;//十位
		for (int i = 0; i < 10; ++i){
			int temp2 = temp + i * 10;
			if (vis[temp2]==0 && prime[temp2]){
				node p; p.num = temp2, p.cnt = now.cnt + 1;
				q[rear++] = p; vis[temp2] = 1;
			}
		}
		temp = now.num / 1000 * 1000 + now.num % 100;//百位
		for (int i = 0; i < 10; ++i){
			int temp2 = temp + i * 100;
			if (vis[temp2]==0 && prime[temp2]){
				node p; p.num = temp2, p.cnt = now.cnt + 1;
				q[rear++] = p; vis[temp2] = 1;
			}
		}
		temp = now.num % 1000;//千位
		for (int i = 1; i < 10; ++i){
			int temp2 = temp + i * 1000;
			if (vis[temp2]==0 && prime[temp2]){
				node p; p.num = temp2, p.cnt = now.cnt + 1;
				q[rear++] = p; vis[temp2] = 1;
			}
		}
	}
	//delete(q);
}
int main(){
	cin >> T;
	int cnt = 0;
	for (int i = 2; i <=20000; ++i){//打表
		int flag = 0;
		for (int j = 2; j < i; ++j){
			if (i%j == 0){
				flag = 1;
				break;
			}
			
		}
		if (!flag)
		prime[i] = 1;
	}
	while (T--){
		cin >> n >> m;
		for (int i = 0; i <= 15000; ++i){
			vis[i] = 0;
			q[i].num = 0, q[i].cnt = 0;
		}
		ans = 0;
		if (n == m){
			cout << 0 << endl; continue;
		}
		bfs();
		if (!ans)cout << "Impossible" << endl;
	}

	return 0;
}

3414 -- Pots

题意:倒水问题,只有6种操作,均只对两个杯子A,B进行操作,只要两个杯子任意一杯的水量=目标水量,即判定找到,6种操作分别是1.倒满A杯,2.倒满B杯,3.倒空A,4.倒空B,5.A倒进B,6.B倒进A。注意:A倒B时,A当前水量>B当前剩余容量(不是水量),则溢出部分需要留在A内,B倒A同理

思路:题目要求最少的操作数,那必然是BFS,每一次都搜索这六种操作即可

注意继承前一个状态所走的路径,我之前忘记继承一直出错

#include<iostream>
using namespace std;
int T;
int n,m,C;
int vis[1000][1000];
struct node{

	int a,b,cnt;
	int s[10000];
};node q[10000];
int main(){
	//while (cin >> n>> m>> C){
	cin >> n >> m >> C;
		int flag = 0;
		
		int pre = 0, rear = 1;
		q[0].a = 0, q[0].b = 0, q[0].cnt = 0;
		vis[0][0] = 1;
		while (pre != rear){
			node now = q[pre++];
			if (now.a == C || now.b == C){
			//	flag = 1;
				cout << now.cnt << endl;
				//1是倒1,2倒2,3是1to2,4是2to1,5是倒空1,6是倒空2
				for (int i = 0; i < now.cnt; ++i){
					if (now.s[i] == 1)cout << "FILL(1)" << endl;
					else if (now.s[i] == 2)cout << "FILL(2)" << endl;
					else if (now.s[i] == 3)cout << "POUR(1,2)" << endl;
					else if (now.s[i] == 4)cout << "POUR(2,1)" << endl;
					else if (now.s[i] == 5)cout << "DROP(1)" << endl;
					else if (now.s[i] == 6)cout << "DROP(2)" << endl;
				}
				return 0;
				break;
			}
			for (int i = 0; i < 6; ++i){
				node temp=now;
				int a = now.a, b = now.b;
				if (i == 0){//倒满a
					if (a == n)continue;
					a = n;
					if (!vis[a][b]){
						temp.s[now.cnt] = 1;
						temp.a = a, temp.b = b, temp.cnt = now.cnt + 1;
						q[rear++] = temp;
						vis[a][b] = 1;
					}
				}
				else if (i == 1){//倒满b
					//if (!b){
					if (b == m)continue;
					b = m;
					if (!vis[a][b]){
						//temp.s = now.s;
						temp.s[now.cnt] = 2;
						temp.a = a, temp.b = b, temp.cnt = now.cnt + 1;
						q[rear++] = temp;
						vis[a][b] = 1;
					}
					//}
				}
				else if (i == 2){//a to b
					int sb = m - b;//b还能放多少
					if (sb==0 || a==0)continue;//b放不下或a没有东西倒
					if (a - sb >= 0){
						a -= sb;
						b = m;
					}
					else{
						b += a;
						a = 0;
					}
					if (!vis[a][b]){
						temp.s[now.cnt] = 3;
						temp.a = a, temp.b = b, temp.cnt = now.cnt + 1;
						q[rear++] = temp;
						vis[a][b] = 1;
					}
				}
				else if (i == 3){//b to a
					int sa = n - a;//a还能放多少
					if (sa==0 || b==0)continue;//a放不下或b为空
					if (b - sa >= 0){//b还有剩余
						b -= sa;
						a = n;
					}
					else{
						a += b;
						b = 0;
					}
					if (!vis[a][b]){
						temp.s[now.cnt] = 4;
						temp.a = a, temp.b = b, temp.cnt = now.cnt + 1;
						q[rear++] = temp;
						vis[a][b] = 1;
					}
				}
				else if (i == 4){//倒空a
					if (a==0)continue;
					a = 0;
					if (!vis[a][b]){
						temp.s[now.cnt] = 5;
						temp.a = a, temp.b = b, temp.cnt = now.cnt + 1;
						q[rear++] = temp;
						vis[a][b] = 1;
					}
				}
				else if (i == 5){//倒空b
					if (b==0)continue;
					b = 0;
					if (!vis[a][b]){
						temp.s[now.cnt] = 6;
						temp.a = a, temp.b = b, temp.cnt = now.cnt + 1;
						q[rear++] = temp;
						vis[a][b] = 1;
					}
				}
			}
		}
		cout << "impossible" << endl;
	//}
	return 0;
}

2531 -- Network Saboteur

题意:给定一张无向联通图,将图中所有节点分成两个集合,求连接两个集合的边最大之和是多少(即最大割问题:怎么把一张图切割成两个部分,并且相连的边之和最大)

ps:有道类似的题可以在力扣搜一下二分图匹配问题

思路:搜索两种状态即可,选择与不选择当前点加入集合1,可剪枝(看代码)

#include<algorithm>
#include<iostream>
using namespace std;
int n;
int maze[30][30],vis[30];
int ans;
void dfs(int now,int sum){

	vis[now] = 1;//加入集合1,共两个集合,集合0和1
	int temp = sum;
	for (int i = 1; i <= n; ++i){
		if (vis[i]){//同是集合1
			temp -= maze[now][i];
		}
		else{
			temp += maze[now][i];
		}
	}
	ans = max(ans, temp);
	if (temp < sum)return;//剪枝,此点加入该集合后使两集合的和变小,则不选择
	for (int i = now + 1; i < n + 1; ++i){
		dfs(i,temp);
		vis[i] = 0;//回溯,本算法的精髓,即选与不选,给自己后悔的路
	}

}
int main(){

	//int n;
	cin >> n;
	for (int i = 1; i <= n; ++i){
		for (int j = 1; j <= n; ++j){
			cin >> maze[i][j];
		}
	}
	dfs(1,0);
	
	cout <<ans <<endl;
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值