最大流

最大流算法

最大流最小割的关系

POJ 3436 题目链接

题意

流水线上有N台机器装电脑,电脑有P个部件,每台机器有三个参数,产量,输入规格,输出规格;输入规格中0表示改部件不能有,1表示必须有,2无所谓;输出规格中0表示改部件没有,1表示有。问如何安排流水线(如何建边)使产量最高。

思路

拆点,将一个机器拆成入点和出点,入点到出点的容量就是边权
设置一个超级源点,超级源点到全0的入点容量为INF,设置超级终点,全1的出点到超级终点的容量为INF,
中间点:出点要和入点的1吻合,不能多不能少,容量设置为INF(出点的1比入点的1多就会出现环)
然后跑一遍最大流

最大流输出路径
#include <queue>
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const double eps = 1e-8;
const int N = 111;
const int M = 1e5 + 5;
struct edge{
    int to,nex,len,old;
}e[M];
struct point{
    int u,v,len;
}res[N];
struct node{
    int value,in[N],out[N];
}s[N];
int head[N],cnt;
int se,ed,p,n;
int depth[N],vis[N];
void add(int u,int v,int len)
{
    e[cnt].len = len;
    e[cnt].to = v;
    e[cnt].nex = head[u];
    e[cnt].old = len;
    head[u] = cnt++;
}
/// 层次网络
int bfs()
{
    memset(depth,0,sizeof(depth));
    queue<int> q;
    q.push(se);
    depth[se] = 1;
    while(!q.empty()){
        int u = q.front();
        q.pop();
        for (int i = head[u]; i != -1; i = e[i].nex){
            int v = e[i].to;
            if (depth[v] || e[i].len <= 0) continue;
            depth[v] = depth[u] + 1;
            q.push(v);
        }
    }
    return depth[ed];
}
/// dfs查找所有增广路并做流量调整
int dfs(int u,int maxflow)
{
    if (u == ed) return maxflow;
    int ans = 0;
    for (int i = head[u]; i != -1 && ans < maxflow; i = e[i].nex){
        int v = e[i].to;
        if (depth[v] != depth[u] + 1 || e[i].len == 0) continue;
        int temflow = dfs(v,min(e[i].len,maxflow - ans));
        e[i].len -= temflow;     /// 前向弧流量减少
        e[i ^ 1].len += temflow;     /// 反向弧流量增加
        ans += temflow;
        if (ans == maxflow) break;
    }
    if (!ans) depth[u] = INF;
    return ans;
}
int dinic()
{
    int ans = 0;
    while(bfs()){
        ans += dfs(se,INF);
    }
    return ans;
}
bool judge(node a,node b)
{
    for (int i = 1; i <= p; i++){
        if ((b.in[i] == 1 && !a.out[i]) || (!b.in[i] && a.out[i])) return false;
    }
    return true;
}
bool link_start(node a)
{
    for (int i = 1; i <= p; i++){
        if (a.in[i] == 1) return false;
    }
    return true;
}
bool link_end(node a)
{
    for (int i = 1; i <= p; i++){
        if (!a.out[i]) return false;
    }
    return true;
}
int main()
{
    while(scanf("%d %d",&p,&n) == 2){
        memset(head,-1,sizeof(head));
        cnt = 0;
        se = 0,ed = 2 * n + 1;
        for (int i = 1; i <= n; i++){
            scanf("%d",&s[i].value);
            for (int j = 1; j <= p; j++){
                scanf("%d",&s[i].in[j]);
            }
            for (int j = 1; j <= p; j++){
                scanf("%d",&s[i].out[j]);
            }
            add(i,i + n,s[i].value);
            add(i + n,i,0);
            if (link_start(s[i])){
                add(se,i,INF);
                add(i,se,0);
            }
            if (link_end(s[i])){
                add(i + n,ed,INF);
                add(ed,i + n,0);
            }
        }
        for (int i = 1; i <= n; i++){
            for (int j = 1; j <= n; j++){
                if (i == j) continue;
                if (judge(s[i],s[j])){
                    add(i + n,j,INF);
                    add(j,i + n,0);
                }
            }
        }
        int mx = dinic();
        int ans = 0;
        for (int u = n + 1; u <= 2 * n; u++){
            for (int i = head[u]; i != -1; i = e[i].nex){
                int  v = e[i].to;
                if (v == 0 || v > n) continue;
                if (v + n == u) continue;
                if (e[i].old > e[i].len){
                    res[ans].u = u - n;
                    res[ans].v = v;
                    res[ans].len = e[i].old - e[i].len;
                    ans++;
                }
            }
        }
        printf("%d %d\n",mx,ans);
        for (int i = 0; i < ans; i++){
            printf("%d %d %d\n",res[i].u,res[i].v,res[i].len);
        }
    }
    return 0;
}

POJ 2516 题目链接

题意

有N个供应商,M个店主,K种物品。每个供应商对每种物品的的供应量已知,每个店主对每种物品的需求量的已知,从不同的供应商运送不同的货物到不同的店主手上需要不同的花费,又已知从供应商Mj送第kind种货物的单位数量到店主Ni手上所需的单位花费。
问:供应是否满足需求?如果满足,最小运费是多少?

思路

如果要将每个点的物品和供应商的物品都弄成点,那点太多了,肯定会超时的,因为物品是独立的,所以我们把每一种物品都跑一遍最小费用最大流然后求和即可

最小费用最大流SPFA模板
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
const int N = 111;
const int M = 1e5 + 7;
const int INF = 0x3f3f3f3f;
struct node{
    int head,to,cost,cap,nex;
}e[M];
int need[N][N],goods[N][N];
int sum_need[N],sum_goods[N];
int cost[N][N][N];
int nv,se,ed;
bool vis[N];
int lowcost[N],pre[N];
int head[N],cnt;
void add(int u,int v,int cap,int cost)
{
    e[cnt].head = u;
    e[cnt].to = v;
    e[cnt].cap = cap;
    e[cnt].cost = cost;
    e[cnt].nex = head[u];
    head[u] = cnt++;
}
bool SPFA()
{
    for (int i = 0; i < nv; i++){
        vis[i] = 0;
        lowcost[i] = INF;
    }
    queue<int> q;
    q.push(se);
    vis[se] = true;
    lowcost[se] = 0;
    while(!q.empty()){
        int u = q.front();
        q.pop();
        vis[u] = false;
        for (int i = head[u]; i != -1; i = e[i].nex){
            int v = e[i].to;
            if (e[i].cap && lowcost[v] > lowcost[u] + e[i].cost){
                lowcost[v] = lowcost[u] + e[i].cost;
                pre[v] = i;
                if (!vis[v]){
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    return lowcost[ed] != INF;
}
int MCMF()
{
    int ans = 0;
    while(SPFA()){
        int flow = INF,cost = 0;
        for (int u = ed; u != se; u = e[pre[u]].head){
            flow = min(flow,e[pre[u]].cap);
        }
        for (int u = ed; u != se; u = e[pre[u]].head){
            e[pre[u]].cap -= flow;
            e[pre[u] ^ 1].cap += flow;
            cost += flow * e[pre[u]].cost;
        }
        ans += cost;
    }
    return ans;
}
bool judge(int k)
{
    for (int i = 1; i <= k; i++){
        if (sum_need[i] > sum_goods[i]) return true;
    }
    return false;
}
int main()
{
    int n,m,p;
    while(scanf("%d %d %d",&n,&m,&p) == 3 && (n || m || p)){
        memset(sum_goods,0,sizeof(sum_goods));
        memset(sum_need,0,sizeof(sum_need));
        for (int i = 1; i <= n; i++){
            for (int j = 1; j <= p; j++){
                scanf("%d",&need[i][j]);
                sum_need[j] += need[i][j];
            }
        }
        for (int i = 1; i <= m; i++){
            for (int j = 1; j <= p; j++){
                scanf("%d",&goods[i][j]);
                sum_goods[j] += goods[i][j];
            }
        }
        for (int k = 1; k <= p; k++){
            for (int i = 1; i <= n; i++){
                for (int j = 1; j <= m; j++){
                    scanf("%d",&cost[k][i][j]);
                }
            }
        }
        if (judge(p)){
            printf("-1\n");
            continue;
        }
        se = 0,ed = n + m + 1,nv = n + m + 2;
        int ans = 0;
        for (int k = 1; k <= p; k++){
            memset(head,-1,sizeof(head));
            cnt = 0;
            for (int i = 1; i <= m; i++){
                add(se,i,goods[i][k],0);
                add(i,se,0,0);
            }
            for (int i = 1; i <= n; i++){
                add(i + m,ed,need[i][k],0);
                add(ed,i,0,0);
            }
            for (int i = 1; i <= n; i++){
                for (int j = 1; j <= m; j++){
                    add(j,i + m,INF,cost[k][i][j]);
                    add(i + m,j,0,-cost[k][i][j]);
                }
            }
            ans += MCMF();
        }
        printf("%d\n",ans);
    }
    return 0;
}

UVA 10480 VJ题目链接

题意

这道题的意思要把一个图分成两部分,要把点1和点2分开。隔断每条边都有一个花费,求最小花费的情况下,应该切断那些边

思路

最小割

最小割输出路径

在残量网络中,将源点S能到达的点看作S集,其他点看作T集。如果边的一个点属于S集,另一个点属于T集,那么该边属于最小割边集。
也就是在depth数组中,属于S集的能够到达,属于T集的不能到达

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
const int N = 4444;
const int M = 1e5 + 7;
const int INF = 0x3f3f3f3f;
struct node{
    int to,len,nex;
}e[M * 2];
int depth[N];
int se,ed;
int head[N],cnt;
void add(int u,int v,int len)
{
    e[cnt].to = v;
    e[cnt].len = len;
    e[cnt].nex = head[u];
    head[u] = cnt++;
}
/// 层次网络
int bfs()
{
    memset(depth,0,sizeof(depth));
    queue<int> q;
    q.push(se);
    depth[se] = 1;
    while(!q.empty()){
        int u = q.front();
        q.pop();
        for (int i = head[u]; i != -1; i = e[i].nex){
            int v = e[i].to;
            if (depth[v] || e[i].len <= 0) continue;
            depth[v] = depth[u] + 1;
            q.push(v);
        }
    }
    return depth[ed];
}
/// dfs查找所有增广路并做流量调整
int dfs(int u,int maxflow)
{
    if (u == ed) return maxflow;
    int ans = 0;
    for (int i = head[u]; i != -1 && ans < maxflow; i = e[i].nex){
        int v = e[i].to;
        if (depth[v] != depth[u] + 1 || e[i].len == 0) continue;
        int temflow = dfs(v,min(e[i].len,maxflow - ans));
        e[i].len -= temflow;     /// 前向弧流量减少
        e[i ^ 1].len += temflow;     /// 反向弧流量增加
        ans += temflow;
        if (ans == maxflow) break;
    }
    if (!ans) depth[u] = INF;
    return ans;
}
int dinic()
{
    int ans = 0;
    while(bfs()){
        ans += dfs(se,INF);
    }
    return ans;
}
int main()
{
    int n,m;
    while(scanf("%d %d",&n,&m) == 2 && (n || m)){
        memset(head,-1,sizeof(head));
        cnt = 0;
        se = 1,ed = 2;
        for (int i = 0; i < m; i++){
            int u,v,len;
            scanf("%d %d %d",&u,&v,&len);
            add(u,v,len);
            add(v,u,len);
        }
        dinic();
        for (int u = 1; u <= n; u++){
            for (int i = head[u]; i != -1; i = e[i].nex){
                int v = e[i].to;
                if (depth[u] && !depth[v]){
                    printf("%d %d\n",u,v);
                }
            }
        }
        printf("\n");
    }
    return 0;
}

HDU 3605 题目链接

题意

给出每个人适合住的星球信息和该星球能住多少人 ,第一行给出n m 代表有 n 个人 m 个星球,然后接下来n行每行m个数字 1代表适合第 i 个星球 0 代表不适合第 i 个星球,最后一行m个数表示第 i 个星球最多可以住多少个人,问是不是所有人都可以住到星球上。

思路

因为人有1e5个,星球只有十个,要是每个人都弄成一个点,肯定就超时了,每个人最多只有(1<<10)种选择,就可以把他们看成一个点,流量就是人数,然后跑最大流即可

#include <cstring>
#include <iostream>
#include <queue>
#include <string>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
const int N = 1e6 + 5;
const int M = 1e6 + 5;
const int INF = 0x3f3f3f3f;
struct edge{
    int to,nex,len;
}e[M];
int head[N],cnt;
int se,ed;
int num[N];
int n,m;
int depth[N];
int input()  
{  
    char ch;  
    int a = 0;  
    while((ch = getchar()) == ' ' || ch == '\n');  
    a += ch - '0';  
    while((ch = getchar()) != ' ' && ch != '\n')  
    {  
        a *= 10;  
        a += ch - '0';  
    }  
    return a;  
}
void add(int u,int v,int len)
{
    e[cnt].len = len;
    e[cnt].to = v;
    e[cnt].nex = head[u];
    head[u] = cnt++;
}
/// 层次网络
int bfs()
{
    memset(depth,0,sizeof(depth));
    queue<int> q;
    q.push(se);
    depth[se] = 1;
    while(!q.empty()){
        int u = q.front();
        q.pop();
        for (int i = head[u]; i != -1; i = e[i].nex){
            int v = e[i].to;
            if (depth[v] || e[i].len <= 0) continue;
            depth[v] = depth[u] + 1;
            q.push(v);
        }
    }
    return depth[ed];
}
/// dfs查找所有增广路并做流量调整
int dfs(int u,int maxflow)
{
    if (u == ed) return maxflow;
    int ans = 0;
    for (int i = head[u]; i != -1 && ans < maxflow; i = e[i].nex){
        int v = e[i].to;
        if (depth[v] != depth[u] + 1 || e[i].len == 0) continue;
        int temflow = dfs(v,min(e[i].len,maxflow - ans));
        e[i].len -= temflow;     /// 前向弧流量减少
        e[i ^ 1].len += temflow;     /// 反向弧流量增加
        ans += temflow;
        if (ans == maxflow) break;
    }
    if (!ans) depth[u] = INF;
    return ans;
}
int dinic()
{
    int ans = 0;
    while(bfs()){
        ans += dfs(se,INF);
    }
    return ans;
}
int main()
{
    while(scanf("%d %d",&n,&m) == 2){
        memset(num,0,sizeof(num));
        for (int i = 1; i <= n; i++){
            int ans = 0,x;
            for (int j = 1; j <= m; j++){
                x = input();
                ans = ans * 2 + x;
            }
            num[ans]++;
        }
        memset(head,-1,sizeof(head));
        cnt = 0;
        int k = 1 << m;
        se = 0,ed = k + 2 * m + 1;
        for (int i = 0; i < k; i++){
            if (num[i] == 0) continue;
            add(se,i + 1,num[i]);
            add(i + 1,se,0);
            for (int j = 1; j <= m; j++){
                if (i & (1 << (j - 1))){
                    add(i + 1,k + j,num[i]);
                    add(k + j,i + 1,0);
                }
            }
        }
        for (int i = 1; i <= m; i++){
            int x;
            scanf("%d",&x);
            add(k + i,k + m + i,x);
            add(k + m + i,k + i,0);
            add(k + m + i,ed,INF);
            add(ed,k + m + i,0);
        }
        if (dinic() == n) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

HDU 3081 题目链接

题意

n个男生,n个女生,接下来有 m个关系,u v表示第 u 个女生和第 v个男生可以配对,然后接下来有 f 个关系,u v表示第 u个女生和第v个女生是好友,如果 u 和 v可以配对,u 和 w是好友,那么w 和 v也是可以配对的.问知道这些人的关系,最多可以完全配对多少次(完全配对是指n个男生和n个女生都可以配对)?

思路

二分+并查集+最大流

#include <cstring>
#include <iostream>
#include <queue>
#include <string>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
const int N = 2222;
const int M = 1e6 + 5;
const int INF = 0x3f3f3f3f;
struct edge{
    int to,nex,len;
}e[M];
int head[N],cnt;
int se,ed;
int pre[N];
int n,m;
int depth[N];
int g[N][N];
void add(int u,int v,int len)
{
    e[cnt].len = len;
    e[cnt].to = v;
    e[cnt].nex = head[u];
    head[u] = cnt++;
}
/// 层次网络
int bfs()
{
    memset(depth,0,sizeof(depth));
    queue<int> q;
    q.push(se);
    depth[se] = 1;
    while(!q.empty()){
        int u = q.front();
        q.pop();
        for (int i = head[u]; i != -1; i = e[i].nex){
            int v = e[i].to;
            if (depth[v] || e[i].len <= 0) continue;
            depth[v] = depth[u] + 1;
            q.push(v);
        }
    }
    return depth[ed];
}
/// dfs查找所有增广路并做流量调整
int dfs(int u,int maxflow)
{
    if (u == ed) return maxflow;
    int ans = 0;
    for (int i = head[u]; i != -1 && ans < maxflow; i = e[i].nex){
        int v = e[i].to;
        if (depth[v] != depth[u] + 1 || e[i].len == 0) continue;
        int temflow = dfs(v,min(e[i].len,maxflow - ans));
        e[i].len -= temflow;     /// 前向弧流量减少
        e[i ^ 1].len += temflow;     /// 反向弧流量增加
        ans += temflow;
        if (ans == maxflow) break;
    }
    if (!ans) depth[u] = INF;
    return ans;
}
int dinic()
{
    int ans = 0;
    while(bfs()){
        ans += dfs(se,INF);
    }
    return ans;
}
void floyd(int n)
{
    for (int k = 1; k <= 2 * n; k++){
        for (int i = 1; i <= 2 * n; i++){
            for (int j = 1; j <= 2 * n; j++){
                if (g[i][k] && g[k][j]) g[i][j] = 1;
            }
        }
    }
}
int Find(int x)
{
    return pre[x] == x ? x : pre[x] = Find(pre[x]);
}
void Union(int a,int b)
{
    int x = Find(a),y = Find(b);
    if (x != y){
        pre[x] = y;
    }
}
void build(int k,int n)
{
    memset(head,-1,sizeof(head));
    cnt = 0;
    for (int i = 1; i <= n; i++){
        add(se,i,k);
        add(i,se,0);
        for (int j = n + 1; j <= n + n; j++){
            if (g[i][j]){
                add(i,j,1);
                add(j,i,0);
            }
        }
    }
    for (int i = n + 1; i <= n + n; i++){
        add(i,ed,k);
        add(ed,i,0);
    }
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= n; j++){
            if (i == j) continue;
            if (Find(i) == Find(j)){
                for (int k = n + 1; k <= n + n; k++){
                    if (!g[j][k] && g[i][k]){
                        add(j,k,1);
                        add(k,j,0);
                        g[j][k] = 1;
                    }
                }
            }
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m,f;
        scanf("%d %d %d",&n,&m,&f);
        se = 0,ed = 2 * n + 1;
        memset(g,0,sizeof(g));
        for (int i = 0; i < m; i++){
            int u,v;
            scanf("%d %d",&u,&v);
            g[u][v + n] = 1;
            g[v + n][u] = 1;
        }
        for (int i = 1; i <= n; i++){
            pre[i] = i;
        }
        for (int i = 0; i < f; i++){
            int u,v;
            scanf("%d %d",&u,&v);
            Union(u,v);
        }
        int l = 0,r = n,ans;
        while(l <= r){
            int mid = (l + r) >> 1;
            build(mid,n);
            if (dinic() == n * mid){
                ans = mid;
                l = mid + 1;
            }
            else r = mid - 1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

HDU 3416 题目链接

题意

在每次都走最短路的情况下,从A市到B市能走多少次(每条路只能用一次)

思路

先跑一遍最短路,把最短路径上的边都找出来(lowcost[v] = lowcost[u] + e[i].len),将这些边的流量都设为1,然后跑一遍最大流即可

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const double eps = 1e-8;
const int N = 1111;
const int M = 1e5 + 5;
struct edge{
    int u,v,len;
}s[M];
struct node{
    int to,nex,len;
}e[M * 4];
struct point{
    int id,cost;
    bool operator < (const point &p) const{
        return cost > p.cost;
    }
};
int head[N],cnt;
int se,ed;
int lowcost[N],depth[N];
void add(int u,int v,int len)
{
    e[cnt].len = len;
    e[cnt].to = v;
    e[cnt].nex = head[u];
    head[u] = cnt++;
}
void dijkstra()
{
    memset(lowcost,0x3f,sizeof(lowcost));
    priority_queue<point> q;
    q.push({se,0});
    lowcost[se] = 0;
    while(!q.empty()){
        point now = q.top();
        q.pop();
        int u = now.id;
        for (int i = head[u]; i != -1; i = e[i].nex){
            int v = e[i].to;
            if (lowcost[v] > lowcost[u] + e[i].len){
                lowcost[v] = lowcost[u] + e[i].len;
                q.push({v,lowcost[v]});
            }
        }
    }
}
/// 层次网络
int bfs()
{
    memset(depth,0,sizeof(depth));
    queue<int> q;
    q.push(se);
    depth[se] = 1;
    while(!q.empty()){
        int u = q.front();
        q.pop();
        for (int i = head[u]; i != -1; i = e[i].nex){
            int v = e[i].to;
            if (depth[v] || e[i].len <= 0) continue;
            depth[v] = depth[u] + 1;
            q.push(v);
        }
    }
    return depth[ed];
}
/// dfs查找所有增广路并做流量调整
int dfs(int u,int maxflow)
{
    if (u == ed) return maxflow;
    int ans = 0;
    for (int i = head[u]; i != -1 && ans < maxflow; i = e[i].nex){
        int v = e[i].to;
        if (depth[v] != depth[u] + 1 || e[i].len == 0) continue;
        int temflow = dfs(v,min(e[i].len,maxflow - ans));
        e[i].len -= temflow;     /// 前向弧流量减少
        e[i ^ 1].len += temflow;     /// 反向弧流量增加
        ans += temflow;
        if (ans == maxflow) break;
    }
    if (!ans) depth[u] = INF;
    return ans;
}
int dinic()
{
    int ans = 0;
    while(bfs()){
        ans += dfs(se,INF);
    }
    return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d %d",&n,&m);
        memset(head,-1,sizeof(head));
        cnt = 0;
        for (int i = 0; i < m; i++){
            scanf("%d %d %d",&s[i].u,&s[i].v,&s[i].len);
            add(s[i].u,s[i].v,s[i].len);
        }
        scanf("%d %d",&se,&ed);
        dijkstra();
        memset(head,-1,sizeof(head));
        cnt = 0;
        for (int i = 0; i < m; i++){
            if (lowcost[s[i].v] == lowcost[s[i].u] + s[i].len){
                add(s[i].u,s[i].v,1);
                add(s[i].v,s[i].u,0);   /// 反向弧为0
            }
        }
        printf("%d\n",dinic());
    }
    return 0;
}

HDU 3338 题目链接

题意

有黑方块和白方块,白方块用来填写数字,黑方块有左下和右上两个三角形,两个三角形都有一个数,左下代表从这块以下到第二个黑方块中的所有白方块数字之和,右上同理,代表的是一行的白方块数字之和

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值