数据结构(陈越)PAT练习题 第六周:图(下)

06-1. Saving James Bond - Hard Version

时间限制
400 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
CHEN, Yue

This time let us consider the situation in the movie "Live and Let Die" in which James Bond, the world's most famous spy, was captured by a group of drug dealers. He was sent to a small piece of land at the center of a lake filled with crocodiles. There he performed the most daring action to escape -- he jumped onto the head of the nearest crocodile! Before the animal realized what was happening, James jumped again onto the next big head... Finally he reached the bank before the last crocodile could bite him (actually the stunt man was caught by the big mouth and barely escaped with his extra thick boot).

Assume that the lake is a 100 by 100 square one. Assume that the center of the lake is at (0,0) and the northeast corner at (50,50). The central island is a disk centered at (0,0) with the diameter of 15. A number of crocodiles are in the lake at various positions. Given the coordinates of each crocodile and the distance that James could jump, you must tell him a shortest path to reach one of the banks. The length of a path is the number of jumps that James has to make.

Input Specification:

Each input file contains one test case. Each case starts with a line containing two positive integers N (<=100), the number of crocodiles, and D, the maximum distance that James could jump. Then N lines follow, each containing the (x, y) location of a crocodile. Note that no two crocodiles are staying at the same position.

Output Specification:

For each test case, if James can escape, output in one line the minimum number of jumps he must make. Then starting from the next line, output the position (x, y) of each crocodile on the path, each pair in one line, from the island to the bank. If it is impossible for James to escape that way, simply give him 0 as the number of jumps. If there are many shortest paths, just output the one with the minimum first jump, which is guaranteed to be unique.

Sample Input 1:
17 15
10 -21
10 21
-40 10
30 -50
20 40
35 10
0 -10
-25 22
40 -40
-30 30
-10 22
0 11
25 21
25 10
10 10
10 35
-30 10
Sample Output 1:
4
0 11
10 21
10 35
Sample Input 2:
4 13
-12 12
12 12
-12 -12
12 -12
Sample Output 2:
0

终于迎来了Hard版本,007的最短路径问题。这个问题主要需要考虑两个方面,一是如何构建图,二是如何计算最短路径。

在上一个Easy版本我说到我的方法,用矩阵来表示湖的每一个位置,这个大家看看就行,不要参考。我当时是被姥姥吓唬到了,说什么图不好建。其实还是可以用常规方法建图的。例如用矩阵或邻接表,把在007跳跃范围内的鳄鱼连接起来。只不过这样略有点费事,每个鳄鱼都要和其它的鳄鱼计算一下距离。假设一共有N条鳄鱼,那么就需要用计算 N(N-1)/2 次,每一次的计算都是勾股定理……这是O(N2)的复杂度,里面有很多次运算并不是必需的,不合算。实际上完全可以在需要找下一条鳄鱼时再去计算这段距离。说到这里,可能你已经想出来了,就是直接用数组!没错!我们直接把每一条鳄鱼的坐标存在数组里,然后每次在数组里寻找跳跃范围内的鳄鱼。在这样的表示方式下同样可以选择用DFS或BFS遍历。

不过现在的问题不是遍历了,而是找出最短路径。这里以跳跃的次数作为路径长度的判断标准,所以它是一个无权图的最短路问题。按照姥姥的思路,可以在BFS算法的基础上作一些改动。添加两个数组 dist 和 path:dist[W] 表示在当前已经遍历过的结点中,从源点S到结点W的最短路径;假设 path[W]=V,那么它表示从源点S到结点W的过程中,需要先经过V点再接着到W点。这个问题需要花时间仔细想一想才能想明白,可以参考姥姥的伪代码把这个过程手动演练一遍,就可以清楚一些。如果还想不明白,就照着伪代码写吧……

不过,问题还没完。题目中有一句话是这样说的:If there are many shortest paths, just output the one with the minimum first jump, which is guaranteed to be unique. 最短路径可能不只一条,在这种情况下,就选择第一次跳最小的那个路径。这个最小是指距离最小,就是离湖心小岛最近的那个鳄鱼。针对这个问题,可以把所有路径都走一遍,然后再把符合条件的那个选出来。这样比较费事。其实可以先把最近的鳄鱼放在前面,也就是说,在最初把小岛周围的那几条鳄鱼收入队列时,把离007最近的那条放在最前边就可以了。如果你是这么想的话,答案还是不对的……假设有 A B C D 四条鳄鱼,A最近,C最远。虽然现在A是最近的,不过从A往后走不一定有出路,或者出路不一定是最短的。正确答案可能是 B C D 中的某一个。所以,这是一个排序问题,需要把这四条鱼全部从近到远排个序才行。但是,做这一题的时候还没讲过排序,我不会排序,怎么办呢?这里我使用了C++ STL里的双端队列deque,当然用双向链表 list 也是可以的,不过要注意STL都是以引用方式插入元素的哦。在某个元素K入队时,先和队里的第一个元素比较,如果K大,就往后挪,直到到队尾或者遇到比较它大的元素,然后插入队列里。这有点类似于插入排序,只不过这是从左往右比较的。这样完成之后,后面的该干啥干啥。(话说STL能节省不少代码啊……)下面上代码:

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

struct loca
{
	int x, y;
}*a;
int n, d;
int* dist;
int* path;
bool bondIsSafe = false;

inline bool centerRange( int k )
{
	return a[k].x*a[k].x + a[k].y*a[k].y <= (7.5+d)*(7.5+d);
}

inline bool inRange( int k1, int k2 )
{
	return (a[k1].x-a[k2].x)*(a[k1].x-a[k2].x) + (a[k1].y-a[k2].y)*(a[k1].y-a[k2].y) <= d*d;
}

inline bool canGetOut( int k )
{
	return (a[k].x+d>=50) || (a[k].x-d<=-50) || (a[k].y+d>=50) || (a[k].y-d<=-50);
}

void outputPath( int t )
{
	if (path[t]==-1)
		cout << a[t].x << ' ' << a[t].y;
	else
	{
		outputPath( path[t] );
		cout << endl << a[t].x << ' ' << a[t].y;
	}
}

int main()
{
	//------ input data ------
	cin >> n >> d;
	dist = new int [n];
	path = new int [n];
	a = new loca [n];
	for (int i=0; i<n; ++i)
	{
		cin >> a[i].x >> a[i].y;
		dist[i] = 0;
	}
	//-------------------------
	if (d+7.5>=50)
	{
		cout << 1;
		return 0;
	}
	//-------------------------
	deque<int> q;
	for (int m=0; m<n; ++m)
	{
		if (!centerRange(m))
			continue;
		int temp = m;
		if (q.empty())
			q.push_back(temp);
		else
		{
			//---- insert -----
			auto i = q.begin();
			while (i!=q.end())
			{
				if (a[m].x*a[m].x+a[m].y*a[m].y < a[*i].x*a[*i].x+a[*i].y*a[*i].y)
					break;
				++i;
			}
			q.insert( i, temp );
		}
		dist[m] = 1;
		path[m] = -1;
	}

	int t;
	while (!q.empty())
	{
		t = q.front();		q.pop_front();
		if( canGetOut(t) )
		{
			bondIsSafe = true;
			break;
		}
		for (int i=0; i<n; ++i)
		{
			if (dist[i]==0 && inRange(t,i))
			{
				dist[i] = dist[t] + 1;
				path[i] = t;
				int temp = i;
				q.push_back(temp);
			}
		}
	}
	//----- output -----
	if (bondIsSafe)
	{
		cout << dist[t]+1 << endl;
		outputPath( t );
	}
	else
		cout << 0;

	return 0;
}


06-2. 旅游规划

时间限制
400 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard

有了一张自驾旅游路线图,你会知道城市间的高速公路长度、以及该公路要收取的过路费。现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径。如果有若干条路径都是最短的,那么需要输出最便宜的一条路径。

输入格式说明:

输入说明:输入数据的第1行给出4个正整数N、M、S、D,其中N(2<=N<=500)是城市的个数,顺便假设城市的编号为0~(N-1);M是高速公路的条数;S是出发地的城市编号;D是目的地的城市编号。随后的M行中,每行给出一条高速公路的信息,分别是:城市1、城市2、高速公路长度、收费额,中间用空格分开,数字均为整数且不超过500。输入保证解的存在。

输出格式说明:

在一行里输出路径的长度和收费总额,数字间以空格分隔,输出结尾不能有多余空格。

样例输入与输出:

序号 输入 输出
1
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20
3 40
2
2 1 0 1
1 0 2 3
2 3

和上一题相比,这是带权图的最短路问题,当然用到那个金光闪闪的 Dijkstra 算法。和无权图的算法相比,对数组 dist 的处理要相对麻烦些。首先它要初始为无穷大。这里初始为无穷大,是为了方便后面比较。其实初始化为负也是可以的,把负数认为是无穷大,只要在比较前先判断其正负性就行。我们把未收录的最小的 dist 的结点V收进来,那么对于V的某个邻接点W(W还未被收录),如果经过V再到W比不经过V而到W要短,就去更新 dist[W] 和 path[W]。这个过程理解起来也是比较困难,还是建议自己动手演练一遍。姥姥全是用文字和语言来讲解这个算法的,大家最好能搜一搜示意图或者动态图,更直观。

针对这一题,图的边有两个权重,长度和路费。要求选择最短长度,如果长度相等就选择收费最少的。乍一看似乎比较麻烦,实际不然。只需在更新 dist 和 path 时,增加一次比较和判断。如果路长相等,就再比较下哪条路收费少就可以了,非常简单。不多说了,直接上代码:

#include <iostream>
using namespace std;

const int LIMIT = 1000000;
int n, m, s, d;
int* dist;
int* path;
int* money;
int* len;
int* cost;
bool* S;

int main()
{
	cin >> n >> m >> s >> d;
	dist = new int [n];
	path = new int [n];
	money= new int [n];
	S    = new bool [n];
	len  = new int [n*n];
	cost = new int [n*n];
	for (int i=0; i<n; ++i)
	{
		for (int j=0; j<n; ++j)
		{
			if (i==j)
				len[i*n+j] = cost[i*n+j] = 0;
			else
				len[i*n+j] = cost[i*n+j] = LIMIT;
		}
		dist[i] = LIMIT;
		path[i] = -1;
		money[i]= LIMIT;
		S[i] = false;
	}

	for (int i=0; i<m; ++i)
	{
		int a, b, c, d;
		cin >> a >> b >> c >> d;
		len[a*n+b]  = len[b*n+a]  = c;
		cost[a*n+b] = cost[b*n+a] = d;
	}
	for (int i=0; i<n; ++i)
	{
		if (len[s*n+i]!=LIMIT)
		{
			dist[i] = len[s*n+i];
			money[i]= cost[s*n+i];
			path[i] = s;
		}
	}
	S[s] = true;
	dist[s] = 0;
	money[s]= 0;

	for (int count=0; count<n-1; ++count)
	{
		int min_dist = LIMIT;
		int min_cost = LIMIT;
		int v = s;
		for (int i=0; i<n; ++i)
		{
			if (!S[i] && dist[i]<min_dist)
			{
				v = i;
				min_dist = dist[i];
				min_cost = money[i];
			}
			else if(!S[i] && dist[i]==min_dist)
			{
				if (money[i]<min_cost)
				{
					v = i;
					min_dist = dist[i];
					min_cost = money[i];
				}
			}
		}
		S[v] = true;
		for (int i=0; i<n; ++i)
		{
			if (!S[i] && len[v*n+i]<LIMIT)
			{
				if (dist[v]+len[v*n+i] < dist[i])
				{
					dist[i] = dist[v] + len[v*n+i];
					money[i] = money[v] + cost[v*n+i];
					path[i] = v;
				}
				else if (dist[v]+len[v*n+i] == dist[i])
				{
					if (money[v]+cost[v*n+i] < money[i])
					{
						dist[i] = dist[v] + len[v*n+i];
						money[i] = money[v] + cost[v*n+i];
						path[i] = v;
					}
				}
			}
		}
	}
	cout << dist[d] << ' ' << money[d];
	return 0;
}


06-3. 公路村村通

时间限制
400 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard

现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。

输入格式说明:

输入数据包括城镇数目正整数N(<=1000)和候选道路数目M(<=3N);随后的M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个城镇的编号以及该道路改建的预算成本。为简单起见,城镇从1到N编号。

输出格式说明:

输出村村通需要的最低成本。如果输入数据不足以保证畅通,则输出-1,表示需要建设更多公路。

样例输入与输出:

序号 输入 输出
1
6 15
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2
2 3 4
2 4 6
2 5 2
2 6 6
3 4 6
3 5 1
3 6 1
4 5 10
4 6 8
5 6 3
12
2
3 1
2 3 2
-1
3
5 4
1 2 1
2 3 2
3 1 3
4 5 4
-1

这是一个最小生成树问题,本质是把一个图里的所有结点,用最短的边连接起来,连接之后就是一棵树。最小生成树有两种算法,Prim 和 Kruskal。Prim 算法有点类似于 Dijkstral 算法,从一个结点出发,每次选择最小的边把相邻的结点连接起来,如果弄懂了 Dijkstral 算法,那 Prim 算法就很好理解;而 Kruskal 算法则是从全局出发,每次都选择最小的边,当然这个边不能破坏树的结构。这两种算法都是贪心算法。如果对这两种不太清楚,就再回去看看姥姥的视频吧!下面是我的代码:

#include <iostream>
using namespace std;

const int LIMIT = 1000000;
int N, M;
int* a;
int* dist;
int* parent;
bool* MST;

int min_dist()
{
	int min_d = LIMIT, index = -1;
	for (int i=0; i<N; ++i)
	{
		if (!MST[i] && dist[i]<min_d)
		{
			min_d = dist[i];
			index = i;
		}
	}
	return index;
}

bool all_in_MST()
{
	bool all_in_MST = true;
	for (int i=0; i<N; ++i)
	{
		if (!MST[i])
		{
			all_in_MST = false;
			break;
		}
	}
	return all_in_MST;
}

int main()
{
	cin >> N >> M;
	a = new int [N*N];
	for (int i=0; i<N; ++i)
		for (int j=0; j<N; ++j)
			a[i*N+j] = (i==j? 0: LIMIT);

	for (int i=0; i<M; ++i)
	{
		int x, y;
		cin >> x >> y;
		--x; --y;
		cin >> a[x*N+y];
		a[y*N+x] = a[x*N+y];
	}
	dist = new int [N];	//--取结点0为初始点
	MST  = new bool [N];
	parent = new int [N];
	for (int i=0; i<N; ++i)
	{
		dist[i] = a[i];	//a[0][i]
		MST[i] = false;

		if (dist[i]<LIMIT)
			parent[i] = 0;
	}
	//-----------------------------------//
	MST[0] = true;
	while (true)
	{
		int v = min_dist();
		if (v==-1)
			break;
		MST[v] = true;
		dist[v] = 0;
		for (int i=0; i<N; ++i)
		{
			if (a[v*N+i]<LIMIT)
			{
				if (dist[i]!=0)
				{
					if (a[v*N+i] < dist[i])
					{
						dist[i] = a[v*N+i];
						parent[i] = v;
					}
				}
			}
		}
	}
	if (!all_in_MST())
		cout << -1;
	else
	{
		int len = 0;
		for (int i=1; i<N; ++i)
			len += a[i*N+parent[i]];
		cout << len;
	}
	return 0;
}


06-4. How Long Does It Take

时间限制
200 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
CHEN, Yue

Given the relations of all the activities of a project, you are supposed to find the earliest completion time of the project.

Input Specification:

Each input file contains one test case. Each case starts with a line containing two positive integers N (<=100), the number of activity check points (hence it is assumed that the check points are numbered from 0 to N-1), and M, the number of activities. Then M lines follow, each gives the description of an activity. For the i-th activity, three non-negative numbers are given: S[i], E[i], and L[i], where S[i] is the index of the starting check point, E[i] of the ending check point, and L[i] the lasting time of the activity. The numbers in a line are separated by a space.

Output Specification:

For each test case, if the scheduling is possible, print in a line its earliest completion time; or simply output "Impossible".

Sample Input 1:
9 12
0 1 6
0 2 4
0 3 5
1 4 1
2 4 1
3 5 2
5 4 0
4 6 9
4 7 7
5 7 4
6 8 2
7 8 4
Sample Output 1:
18
Sample Input 2:
4 5
0 1 1
0 2 2
2 1 3
1 3 4
3 2 5
Sample Output 2:
Impossible

这一题是关键路径问题,要求给出最短工期。嗯,怎么说好呢……姥姥已经把这个问题说得很清楚了……在这个问题中,需要注意的是,一道工序的结束点可能有多个,或者说,可能有多个结点的出度都为0。所以,整个工期的长短,是以最大的结束点的天数为准的,即最大的那个 Earliest[i]。其它的就参考姥姥的伪代码来写就行了。

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

int N, M;
int* a;
int* earliest;
int* indegree;

int main()
{
	cin >> N >> M;
	a = new int [N*N];
	earliest = new int [N];
	indegree = new int [N];
	for (int i=0; i<N; ++i)
	{
		for (int j=0; j<N; ++j)
			a[i*N+j] = -1;
		indegree[i] = 0;
		earliest[i] = 0;
	}
	//-------------------------------//
	for (int i=0; i<M; ++i)
	{
		int x, y;
		cin >> x >> y;
		cin >> a[x*N+y];
		++indegree[y];
	}
	//===========================================//
	int len = 0;
	queue<int> q;
	for (int i=0; i<N; ++i)
		if (indegree[i]==0)
			q.push( i );

	int count = 0;
	while (!q.empty())
	{
		int v = q.front();
		q.pop();
		++count;
		for (int i=0; i<N; ++i)
		{
			if (a[v*N+i]!=-1)
			{
				if (--indegree[i]==0)
					q.push( i );
				earliest[i] = max( earliest[i], earliest[v]+a[v*N+i] );
				if (earliest[i] > len)
					len = earliest[i];
			}
		}
	}
	if (count!=N)
		cout << "Impossible";
	else
		cout << len;

	return 0;
}


06-5. 关键活动

时间限制
400 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard

本实验项目是实验项目6-06的深化。任务调度问题中,如果还给出了完成每个子任务需要的时间,则我们可以算出完成整个工程需要的最短时间。在这些子任务中,有些任务即使推迟几天完成,也不会影响全局的工期;但是有些任务必须准时完成,否则整个项目的工期就要因此延误,这种任务就叫“关键活动”。

请编写程序判定一个给定的工程项目的任务调度是否可行;如果该调度方案可行,则计算完成整个工程项目需要的最短时间,并输出所有的关键活动。

输入格式说明:

输入第1行给出两个正整数N(<=100)和M,其中N是任务交接点(即衔接相互依赖的两个子任务的节点,例如:若任务2要在任务1完成后才开始,则两任务之间必有一个交接点)的数量,交接点按1~N编号,M是子任务的数量,依次编号为1~M。随后M行,每行给出了3个正整数,分别是该任务开始和完成涉及的交接点编号以及该任务所需的时间,整数间用空格分隔。

输出格式说明:

如果任务调度不可行,则输出0;否则第1行输出完成整个工程项目需要的时间,第2行开始输出所有关键活动,每个关键活动占一行,按格式“V->W”输出,其中V和W为该任务开始和完成涉及的交接点编号。关键活动输出的顺序规则是:任务开始的交接点编号小者优先,起点编号相同时,与输入时任务的顺序相反。如下面测试用例2中,任务<5,7>先于任务<5,8>输入,而作为关键活动输出时则次序相反。

样例输入与输出:

序号 输入 输出
1
7 8
1 2 4
1 3 3
2 4 5
3 4 3
4 5 1
4 6 6
5 7 5
6 7 2
17
1->2
2->4
4->6
6->7
2
9 11
1 2 6
1 3 4
1 4 5
2 5 1
3 5 1
4 6 2
5 7 9
5 8 7
6 8 4
7 9 2
8 9 4
18
1->2
2->5
5->8
5->7
7->9
8->9
3
11 14
1 2 4
1 3 3
2 4 5
3 4 3
4 5 1
4 6 6
5 7 5
6 7 2
8 3 7
9 3 7
9 10 6
4 10 2
10 6 5
6 11 4
21
3->4
4->10
6->11
8->3
9->3
10->6
4
4 5
1 2 4
2 3 5
3 4 6
4 2 3
4 1 2
0

如果直接拿上一题的代码过来修改,会发现连工期的时间都算不对。为啥捏?因为上一题的结点是从0开始,而这一题是从1开始的,当然不对!真是坑啊有木有……T_T……让我找了好几次才找到这个茬……这一题在上一题的基础上,不仅要求给出最短工期,还要给出关键路径,也就是机动时间为0的边。这个图需要遍历两次:从工期的开始到结束遍历一次,算出最短时间;再从结束到开始反向遍历一次,以算出关键路径。 我这里用数组 indegree 和 outdegree 分别表示每个节点的入度与出度。要注意这道题奇葩的输出顺序要求,关键路径 a->b,a要按从小到大的顺序输出,而b要按从大到小的顺序输出。来看代码吧!

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

const int LIMIT = 1000000;
int N, M;
int* a;
int* d;
int* earliest;
int* latest;
int* indegree;
int* outdegree;

int main()
{
	cin >> N >> M;
	a = new int [N*N];
	d = new int [N*N];
	earliest = new int [N];
	latest   = new int [N];
	indegree = new int [N];
	outdegree= new int [N];
	for (int i=0; i<N; ++i)
	{
		for (int j=0; j<N; ++j)
		{
			a[i*N+j] = -1;
			d[i*N+j] = 0;
		}
		indegree[i] = 0;
		earliest[i] = 0;
		outdegree[i]= 0;
		latest[i] = LIMIT;
	}
	//-------------------------------//
	for (int i=0; i<M; ++i)
	{
		int x, y;
		cin >> x >> y;
		--x; --y;
		cin >> a[x*N+y];
		++indegree[y];
		++outdegree[x];
	}
	//===========================================//
	int len = 0;
	queue<int> q;
	for (int i=0; i<N; ++i)
		if (indegree[i]==0)
			q.push( i );

	int count = 0;
	while (!q.empty())
	{
		int v = q.front();
		q.pop();
		++count;
		for (int i=0; i<N; ++i)
		{
			if (a[v*N+i]!=-1)
			{
				if (--indegree[i]==0)
					q.push( i );
				earliest[i] = max( earliest[i], earliest[v]+a[v*N+i] );
				if (earliest[i] > len)
					len = earliest[i];
			}
		}
	}
	if (count!=N)
	{
		cout << 0;
		return 0;
	}
	else
		cout << len;
	//============================================================================//
	for (int i=0; i<N; ++i)
		if (outdegree[i]==0)
		{
			q.push( i );
			latest[i] = len;
		}
	while (!q.empty())
	{
		int v = q.front();
		q.pop();
		for (int i=0; i<N; ++i)
		{
			if (a[i*N+v]!=-1)
			{
				latest[i] = min( latest[i], latest[v]-a[i*N+v] );
				if (--outdegree[i]==0)
					q.push( i );
			}
		}
	}
	for (int i=0; i<N; ++i)
		for (int j=N-1; j>=0; --j)
		{
			if (i==j) continue;
			d[i*N+j] = latest[j] - earliest[i] - a[i*N+j];
			if (a[i*N+j]!=-1 && d[i*N+j]==0)
				cout << endl << i+1 << "->" << j+1;
		}

	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值