实验10 图最短路径与拓扑排序


A. DS图应用–最短路径(含代码框架)

题目描述

给出一个图的邻接矩阵,再给出指定顶点v0,求顶点v0到其他顶点的最短路径

代码框架如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

输入

第一行输入t,表示有t个测试实例

第二行输入n,表示第1个图有n个结点

第三行起,每行输入邻接矩阵的一行,以此类推输入n行

第i个结点与其他结点如果相连则为1,无连接则为0,数据之间用空格隔开

第四行输入v0,表示求v0到其他顶点的最短路径距离

以此类推输入下一个示例

输出

每行输出v0到某个顶点的最短距离和最短路径

每行格式:v0编号-其他顶点编号----[最短路径],具体请参考示范数据

输入样例1

1
5
0 5 0 7 15
0 0 5 0 0
0 0 0 0 1
0 0 2 0 0
0 0 0 0 0
0

输出

0-1-5----[0 1 ]
0-2-9----[0 3 2 ]
0-3-7----[0 3 ]
0-4-10----[0 3 2 4 ]

代码

#include <bits/stdc++.h>

using namespace std;
const int MaxLen = 20;
const int MaxDist = 9999;

class Map {
private:
    int Matrix[MaxLen][MaxLen];
    int Vexnum;
public:
    void SetMatrix(int vnum, int mx[MaxLen][MaxLen]);

    void ShortestPath_DIJ(int v0);
};

void Map::SetMatrix(int vnum, int mx[MaxLen][MaxLen]) {
    int i, j;
    Vexnum = vnum;
    for (i = 0; i < Vexnum; i++)
        for (j = 0; j < Vexnum; j++)
            Matrix[i][j] = MaxDist;

    for (i = 0; i < Vexnum; i++)
        for (j = 0; j < Vexnum; j++)
            if (mx[i][j]) Matrix[i][j] = mx[i][j];
}

void Map::ShortestPath_DIJ(int v0) {
    int i, j, v, w, min;
    int *dist = new int[Vexnum];
    bool *final = new bool[Vexnum];
    int path[MaxLen][MaxLen];
    int len[MaxLen];
    for (j = 0; j < Vexnum; j++) {
        final[j] = false;
        dist[j] = Matrix[v0][j];
    }

    for (i = 0; i < Vexnum; i++)
        for (j = 0; j < Vexnum; j++)
            path[i][j] = -1;

    for (i = 0; i < Vexnum; i++) {
        if (dist[i] < MaxDist) {
            path[i][1] = i;
            path[i][0] = v0;
        }
        len[i] = 2;
    }

    dist[v0] = 0;
    final[v0] = true;
    for (i = 0; i < Vexnum; i++) {
        min = MaxDist;
        for (w = 0; w < Vexnum; w++)
            if (!final[w])
                if (dist[w] < min) {
                    v = w;
                    min = dist[w];
                }
        final[v] = true;
        for (w = 0; w < Vexnum; w++) {
            if (!final[w] && (min + Matrix[v][w] < dist[w])) {
                len[w] = 0;
                dist[w] = min + Matrix[v][w];
                for (j = 0; path[v][j] != -1; j++) {
                    path[w][j] = path[v][j];
                    len[w]++;
                }
                path[w][j] = w;
                len[w]++;
            }
        }
    }
    for (i = 0; i < Vexnum; i++) {
        if (i != v0) {
            cout << v0 << '-' << i << '-' << dist[i] << "----" << '[';
            for (j = 0; j < len[i]; j++)
                cout << path[i][j] << ' ';
            cout << ']' << endl;
        }
    }
    delete[]dist;
    delete[]final;
}

int main() {
    int i, j, k, t;
    int vnum, v0;
    int mx[MaxLen][MaxLen];
    Map myMap;
    cin >> t;
    while (t--) {
        for (i = 0; i < MaxLen; i++)
            for (j = 0; j < MaxLen; j++)
                mx[i][j] = 0;
        cin >> vnum;
        for (i = 0; i < vnum; i++)
            for (j = 0; j < vnum; j++)
                cin >> mx[i][j];
        myMap.SetMatrix(vnum, mx);
        cin >> v0;
        myMap.ShortestPath_DIJ(v0);
    }
    return 0;
}

B. 拓扑排序-STL版

题目描述

已知有向图,顶点从0开始编号,求它的求拓扑有序序列。

拓扑排序算法:给出有向图邻接矩阵
1.逐列扫描矩阵,找出入度为0且编号最小的顶点v

2.输出v,并标识v已访问

3.把矩阵第v行全清0

重复上述步骤,直到所有顶点输出为止

/
// STL::vector基本用法
1)
vector::push_back(1); //将1增加到数组尾部
vector::push_back(e); //将类型为E的一个对象e增加到数组尾部.其中类型E可以自行定义,如Vertex类,int类等。
2.1)
vector vec;
int x = vec[1]; //读取组数v(索引从0开始)的第1个元素
2.2)
vector v;
int& x = v[1]; //引用组数v的第1个元素,注意此处的引用&的用法
x = 10; //修改组数v的第1个元素为10
3)

vector<vector > adjMat; //声明一个矩阵对象adjMat
vector row; //声明一个vector(动态数组)对象row表示矩阵的一行
int j=0;
for(; j<n; ++j) {
int edge;
cin >> edge;
row.push_back(edge); //将边信息增加到数组row的最后
}

this->adjMat.push_back(row); //将row增加到数组adjMat的最后

//

//参考代码框架

#include
#include
using namespace std;
class Graph {
public:
vector isFinished; //索引号所指示的顶点是否已处理过
vector<vector > adjMat; //邻接矩阵
int n; //顶点数 as 成员变量
public:
void readAdjMatrix() {
//从输入读入邻接矩阵,存入this->adjMat
cin >> this->n; //顶点数
int i=0;
for(; i<n; ++i) {
//TODO:设置this->isFinished数组:每个顶点未曾访问过
//提示:调用vector::push_back方法
vector row;
int j=0;
for(; j<n; ++j) {
int edge;
cin >> edge; //读入顶点对i,j之间是否有一条边
//TODO:将边信息增加入row
}
//TODO:将row增加入this->adjMat
//提示:以row为参数,调用adjMat的push_back方法
}
}
bool isZeroInDegrees(int vertexNo) {
//判定顶点vertexNo是否没有入度
int i=0;
//this->adjMat[i][vertexNo] == 0
//表示顶点i与vertexNo之间没有边相连
for(; i<n && this->adjMat[i][vertexNo] == 0; ++i);
//TODO:返回true if 顶点vertexNo没有入度; false [otherwise]
}
int select() {
//从所有顶点中,选出一个顶点i,满足:
//1) i没有处理过,即isFinished[i]=false
//2) i的入度为0
int i = 0;
for (; i < n; ++i) {
//TODO:若顶点i的已经处理过,则pass
//TODO:若顶点度>0,则pass
//提示:调用isZeroInDegrees
//TODO: 设置顶点i已经处理过,即isFinished[i]为正确值
//TODO: 返回i
}
//TODO: 返回-1, 表示未找到满足1)+2)条件的顶点
}
void update(int rootNo) {
//更新顶点rootNo的出弧,即将顶点rootNo从图中断开
int j=0;
for(;j<n; ++j) {
//TODO: 设置adjMat[rootNo][j]为0
}
}
/
// 拓扑排序主算法
void topologySort() {
int i=0;
for(; i<n; ++i) { //遍历n次:即按拓扑序,依次选择(排出)n个顶点
int root; // 声明顶点索引号(编号)用于存放本次选出的顶点
//TODO: 调用select方法,并将其返回值存入root
//TODO: 若root=-1,则break;
// root=-1表示没有可以排出的顶点
//TODO: 以root为参数,调用update方法
//TODO:输出当前选出的顶点root 和一个空格
}
//TODO:输出一行
}
void main() {
readAdjMatrix();
topologySort();
}
};
int main() {
int t;
cin >> t;
while (t–) {
Graph g;
g.main();
}
return 0;
}

输入

第一行输入一个整数t,表示有t个有向图

第二行输入n,表示图有n个顶点

第三行起,输入n行整数,表示图对应的邻接矩阵

以此类推输入下一个图的顶点数和邻接矩阵

输出

每行输出一个图的拓扑有序序列

输入样例1

2
5
0 1 0 1 1
0 0 1 0 0
0 0 0 0 1
0 0 1 0 0
0 0 0 0 0
7
0 0 0 0 0 0 0
1 0 1 1 0 0 0
1 0 0 0 0 0 0
1 0 1 0 0 0 0
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 0 0 1 0 1 0

输出

0 1 3 2 4
4 6 5 1 3 2 0

代码

#include <iostream>
#include <vector>

using namespace std;

class Graph {
public:
    vector<bool> isFinished;      //索引号所指示的顶点是否已处理过
    vector<vector<int> > adjMat;  //邻接矩阵
    int n;                        //顶点数 as 成员变量
public:
    void readAdjMatrix() {
        //从输入读入邻接矩阵,存入this->adjMat
        cin >> this->n;           //顶点数
        int i, j;
        for (i = 0; i < n; ++i) {
            for (int k = 0; k < n; k++)
                isFinished.push_back(false);
            //TODO:设置this->isFinished数组:每个顶点未曾访问过
            //提示:调用vector::push_back方法
            vector<int> row;
            for (j = 0; j < n; ++j) {
                int edge;
                cin >> edge;    //读入顶点对i,j之间是否有一条边
                row.push_back(edge);
                //TODO:将边信息增加入row
            }
            this->adjMat.push_back(row);
            //TODO:将row增加入this->adjMat
            //提示:以row为参数,调用adjMat的push_back方法
        }
    }

    bool isZeroInDegrees(int vertexNo) {
        //判定顶点vertexNo是否没有入度
        int i;
        //this->adjMat[i][vertexNo] == 0
        //表示顶点i与vertexNo之间没有边相连
        for (i = 0; i < n && this->adjMat[i][vertexNo] == 0; ++i){}
        if(i>=n) return true;
        else return false;
        //TODO:返回true if 顶点vertexNo没有入度; false [otherwise]
    }

    int select() {
        //从所有顶点中,选出一个顶点i,满足:
        //1) i没有处理过,即isFinished[i]=false
        //2) i的入度为0
        int i = 0;
        for (; i < n; ++i) {
            if(isFinished[i]) continue;
            //TODO:若顶点i的已经处理过,则pass
            if(!isZeroInDegrees(i)) continue;
            //TODO:若顶点度>0,则pass
            //提示:调用isZeroInDegrees
            isFinished[i]=true;
            //TODO: 设置顶点i已经处理过,即isFinished[i]为正确值
            return i;
            //TODO: 返回i
        }
        return -1;
        //TODO: 返回-1, 表示未找到满足1)+2)条件的顶点
    }

    void update(int rootNo) {
        //更新顶点rootNo的出弧,即将顶点rootNo从图中断开
        int j = 0;
        for (; j < n; ++j) {
            adjMat[rootNo][j]=0;
            //TODO: 设置adjMat[rootNo][j]为0
        }
    }

    /
    // 拓扑排序主算法
    void topologySort() {
        int i = 0;
        for (; i < n; ++i) {  //遍历n次:即按拓扑序,依次选择(排出)n个顶点
            int root;  // 声明顶点索引号(编号)用于存放本次选出的顶点
            root=select();
            //TODO: 调用select方法,并将其返回值存入root
            if(root==-1) break;
            //TODO: 若root=-1,则break;
            // root=-1表示没有可以排出的顶点
            update(root);
            //TODO: 以root为参数,调用update方法
            cout<<root<<' ';
            //TODO:输出当前选出的顶点root 和一个空格
        }
        cout<<endl;
        //TODO:输出一行
    }

    void main() {
        readAdjMatrix();
        topologySort();
    }
};

int main() {
    int t;
    cin >> t;
    while (t--) {
        Graph g;
        g.main();
    }
    return 0;
}

C. 关键路径-STL版

题目描述

给定有向图无环的边信息,求每个顶点的最早开始时间、最迟开始时间。

// 参考代码

#include
#include
#include
#include
using namespace std;

class Vertex {
public:
int indexNo;
bool hasEnterQueue;
int early;
int later;

Vertex(int indexNo) {
    this->indexNo = indexNo;
    this->hasEnterQueue = false;
    early = -1;
    later = 0x7FFFF;
}
void updateEarly(int parentEarly, int edgeValue) {
    int newEarly = parentEarly + edgeValue;
    if (newEarly > this->early)
        this->early = newEarly;
}
void updateLater(int childLater, int edgeValue) {
    int newLater = childLater - edgeValue;
    if (newLater < this->later)
        this->later = newLater;
}

};

class Graph {
public:
vector vertexes;
vector<vector > adjMat;
int n;
public:
void readVertexes() {
//TODO: 将顶点数读入成员变量n

    //TODO: 从输入初始化vertexes数组
    int i=0;
    for(; i<n; ++i) {
        Vertex v(i);
        this->vertexes.push_back(v);
    }
    
    //为成员变量adjMat创建内存,赋初值
    for(i=0; i<n; ++i) {
        vector<int> row;
        int j=0;
        for(; j<n; ++j) {
            //TODO: 将0增加到row最后
        }
       //TODO: 将row增加到adjMat最后
    }
}
void readAdjMatrix() {
    //read the adjacent info into this->adjMat
    int edges;
    cin >> edges;
    int i=0;
    int s, t, w;  //s源顶点编号,t目的顶点编号,w边长
    for(; i<edges; ++i) {
        //TODO: 读入s,t,w,并将adjMat的第s行、第t列的值改为w.
    }
}

void updateEarly(int parentNo, queue<int>& earlyQue) {
    int parentEarly = vertexes[parentNo].early;  //读入父结点early值

    int j=0;
    for(; j<n; ++j) {
        int edgeValue = adjMat[parentNo][j];
        if (edgeValue == 0) continue;  //若父结点与结点j没有边相连,pass

        Vertex& child = vertexes[j];
        child.updateEarly(parentEarly, edgeValue); //更新子结点j的early信息

        if(!child.hasEnterQueue) {
            child.hasEnterQueue = true; //将子结点加入队列
            earlyQue.push(j);
        }
    }
}
void updateLater(int childNo, queue<int>& laterQue) {
    //TODO:
}

int getRoot() {
    //获取入度为0的顶点
    int j=0;
    for(; j<n; ++j) {
        int i=0;
        for(; i<n && adjMat[i][j] == 0; ++i);
        if (i>=n) return j; //j has not any in-edges.
    }
    return -1;  //表示没找到
}
int getLeaf() {
    //TODO: 获取出度为0的顶点
}

void printEarlyLater(bool isEarly) {
    int i=0;
    for(; i<n; ++i) {
        Vertex& v = vertexes[i];
        if (isEarly)
            cout << v.early << " ";
        else {
            cout << v.later << " ";
        }
    }
    cout << endl;
}

void findEarly() {
    //执行关键路径算法,求每个顶点的最早开始时间。
    int r = getRoot();
    Vertex& root = vertexes[r];
    root.hasEnterQueue = true;
    root.early = 0;

    queue<int> que;
    que.push(r);

    while(!que.empty()) {
        int p = que.front();
        que.pop();

        updateEarly(p, que);
    }

    printEarlyLater(true);
}
void clearEnterQueue() {
    int i=0;
    for(; i<n; ++i) {
        vertexes[i].hasEnterQueue = false;
    }
}
void findLater() {
    //TODO:调用clearEnterQueue,以清除每个顶点的hasEnterQueue=false
    //执行关键路径算法,求每个顶点的最迟开始时间。
}

void main() {
    readVertexes();
    readAdjMatrix();
    findEarly();
    findLater();
}

};

int main() {
int t=1;
//cin >> t;
while (t–) {
Graph g;
g.main();
}
return 0;
}

输入

第一行图的顶点总数

第二行边的总数

第三行开始,每条边的时间长度,格式为源结点 目的结点 长度

输出

第一行:第个顶点的最早开始时间

第二行:每个顶点的最迟开始时间

输入样例1

9
12
0 1 3
0 2 10
1 3 9
1 4 13
2 4 12
2 5 7
3 6 8
3 7 4
4 7 6
5 7 11
6 8 2
7 8 5

输出

0 3 10 12 22 17 20 28 33
0 9 10 23 22 17 31 28 33

代码

#include <iostream>
#include <vector>
#include <string>
#include <queue>

using namespace std;

class Vertex {
public:
    int indexNo;
    bool hasEnterQueue;
    int early;
    int later;

    Vertex(int indexNo) {
        this->indexNo = indexNo;
        this->hasEnterQueue = false;
        early = -1;
        later = 0x7FFFF;
    }

    void updateEarly(int parentEarly, int edgeValue) {
        int newEarly = parentEarly + edgeValue;
        if (newEarly > this->early)
            this->early = newEarly;
    }

    void updateLater(int childLater, int edgeValue) {
        int newLater = childLater - edgeValue;
        if (newLater < this->later)
            this->later = newLater;
    }
};


class Graph {
public:
    vector<Vertex> vertexes;
    vector<vector<int> > adjMat;
    int n;
public:
    void readVertexes() {
        cin >> this->n;
        //TODO: 将顶点数读入成员变量n
        //TODO: 从输入初始化vertexes数组
        int i;
        for (i = 0; i < n; ++i) {
            Vertex v(i);
            this->vertexes.push_back(v);
        }

        //为成员变量adjMat创建内存,赋初值
        for (i = 0; i < n; ++i) {
            vector<int> row;
            int j = 0;
            for (; j < n; ++j) {
                row.push_back(0);
                //TODO: 将0增加到row最后
            }
            adjMat.push_back(row);
            //TODO: 将row增加到adjMat最后
        }
    }

    void readAdjMatrix() {
        //read the adjacent info into this->adjMat
        int edges;
        cin >> edges;
        int i = 0;
        int s, t, w;  //s源顶点编号,t目的顶点编号,w边长
        for (; i < edges; ++i) {
            cin >> s >> t >> w;
            adjMat[s][t] = w;
            //TODO: 读入s,t,w,并将adjMat的第s行、第t列的值改为w.
        }
    }

    void updateEarly(int parentNo, queue<int> &earlyQue) {
        int parentEarly = vertexes[parentNo].early;  //读入父结点early值
        int j = 0;
        for (; j < n; ++j) {
            int edgeValue = adjMat[parentNo][j];
            if (edgeValue == 0) continue;  //若父结点与结点j没有边相连,pass
            Vertex &child = vertexes[j];
            child.updateEarly(parentEarly, edgeValue); //更新子结点j的early信息

            if (!child.hasEnterQueue) {
                child.hasEnterQueue = true; //将子结点加入队列
                earlyQue.push(j);
            }
        }
    }

    void updateLater(int childNo, queue<int> &laterQue) {
        int childlater = vertexes[childNo].later;  //读入父结点early值
        int j = 0;
        for (; j < n; ++j) {
            int edgeValue = adjMat[j][childNo];
            if (edgeValue == 0) continue;  //若子结点与结点j没有边相连,pass
            Vertex &father = vertexes[j];
            father.updateLater(childlater, edgeValue);//更新父结点j的later信息

            if (!father.hasEnterQueue) {
                father.hasEnterQueue = true; //将fu结点加入队列
                laterQue.push(j);
            }
        }
        //TODO:
    }

    int getRoot() {
        //获取入度为0的顶点
        int j = 0;
        for (; j < n; ++j) {
            int i = 0;
            for (; i < n && adjMat[i][j] == 0; ++i);
            if (i >= n) return j; //j has not any in-edges.
        }
        return -1;  //表示没找到
    }

    int getLeaf() {
        int i, j;
        for (i = 0; i < n; i++) {
            for (j = 0; j < n && adjMat[i][j] == 0; j++);
            if (j >= n) return i;
        }
        return -1;
        //TODO: 获取出度为0的顶点
    }

    void printEarlyLater(bool isEarly) {
        int i = 0;
        for (; i < n; ++i) {
            Vertex &v = vertexes[i];
            if (isEarly)
                cout << v.early << " ";
            else {
                cout << v.later << " ";
            }
        }
        cout << endl;
    }

    void findEarly() {
        //执行关键路径算法,求每个顶点的最早开始时间。
        int r = getRoot();
        Vertex &root = vertexes[r];
        root.hasEnterQueue = true;
        root.early = 0;

        queue<int> que;
        que.push(r);

        while (!que.empty()) {
            int p = que.front();
            que.pop();
            updateEarly(p, que);
        }
        printEarlyLater(true);
    }

    void clearEnterQueue() {
        int i = 0;
        for (; i < n; ++i) {
            vertexes[i].hasEnterQueue = false;
        }
    }

    void findLater() {
        int i;
        clearEnterQueue();
        int L = getLeaf();
        Vertex &leaf = vertexes[L];
        leaf.hasEnterQueue = true;
        leaf.later = leaf.early;
        queue<int> que;
        que.push(L);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            updateLater(p, que);
        }
        printEarlyLater(false);
        //TODO:调用clearEnterQueue,以清除每个顶点的hasEnterQueue=false
        //TODO:执行关键路径算法,求每个顶点的最迟开始时间。
    }

    void main() {
        readVertexes();
        readAdjMatrix();
        findEarly();
        findLater();
    }
};


int main() {
    int t = 1;
    while (t--) {
        Graph g;
        g.main();
    }
    return 0;
}

D. 道路建设 (Ver. I)

题目描述

有N个村庄,编号从1到N,你应该建造一些道路,使每个村庄都可以相互连接。

两个村A和B是相连的,当且仅当A和B之间有一条道路,或者存在一个村C使得在A和C之间有一条道路,并且C和B相连。

现在一些村庄之间已经有一些道路,你的任务就是修建一些道路,使所有村庄都连通起来,并且所有道路的长度总和是最小的。

输入

测试数据有多组

第一行是整数N(3 <= N <= 100),代表村庄的数量。 然后是N行,其中第i行包含N个整数,这些N个整数中的第j个是村庄i和村庄j之间的距离(距离是[1,1000]内的整数)。

然后是整数Q(0 <= Q <= N *(N + 1)/ 2),接下来是Q行,每行包含两个整数a和b(1 <= a <b <= N),代表着村庄a和村庄b之间的道路已经建成。

输出

对于每组测试数据

输出一个整数,表示要构建的道路的长度总和最小值

输入样例1

3
0 990 692
990 0 179
692 179 0
1
1 2

输出

179

代码

#include <bits/stdc++.h>
using namespace std;
const int MaxLen = 100;
const int inf = 9999;
struct closEdge{
    int lowCost;
    int point;
};
class Graph{
    int Mat[MaxLen][MaxLen];
    int n;
public:
    void readGraph(int m){
        this->n= m;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                cin>>Mat[i][j];
                if(!Mat[i][j])
                    Mat[i][j] = inf;
            }
        }
    }
    void origin_road(){
        int m;
        cin>>m;
        for (int i = 0; i < m; ++i) {
            int a,b;
            cin>>a>>b;
            Mat[a-1][b-1] =0;
            Mat[b-1][a-1] =0;
        }
        prime();
    }
    void prime(){
        int start =0;
        closEdge c[n];
        int sum=0;
        for (int i = 0; i < n; ++i) {
            if(i!=start){
                c[i].point = start;
                c[i].lowCost = Mat[start][i];
            }
        }
        c[start].lowCost = -1;
        for (int i = 1; i < n; ++i) {
            int min = inf;
            for (int j = 0; j < n; ++j) {
                if(c[j].lowCost==-1) {
                    continue;
                }
                if(c[j].lowCost<min){
                    start = j;
                    min = c[j].lowCost;
                }
            }
            sum+=min;
            c[start].lowCost = -1;
            for (int j = 0; j < n; ++j) {
                if(Mat[start][j]<c[j].lowCost){
                    c[j].point = start;
                    c[j].lowCost = Mat[start][j];
                }
            }
        }
        cout<<sum<<endl;
    }

};
int main(){
    int n;
    while (cin>>n) {
        Graph g;
        g.readGraph(n);
        g.origin_road();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值