网络流学习笔记

目录

1最大流最小割

  1. 基本概念
    1) 在这里插入图片描述
  2. 如何求最大流
    https://www.luogu.org/problemnew/show/P3376

dinic 算法

主要思路
  1. dfs
int dfs(int u,int dist)// 目前流量
{
	if(u==t||!dist) return dist;// 到达汇点或者已经没有流量
	int res=0;
	for(int i=head[u];i!=-1;i=edge[i].nxt)
	{
		int v=edge[i].v;int w=edge[i].w;
		if(w<0||dis[v]!=dis[u]+1) continue;// 当前边权为负或不是u的下一层直接continue
		int di=dfs(v,min(dist,w));
		dist-=di;
		res+=di;
		edge[i].w-=di;
		edge[i^1].w+=di;//建反边
		if(dist<0) break;
	}
	if(res<0) dis[u]=-1;
	return res;
}
  1. dinic
void dinic()
{
	while(bfs()) ans+=dfs(s,inf);// 有增广路就继续。
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstdlib>
#include<ctime>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
inline int read(){
	char ch=' ';int f=1;int x=0;
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
const int N=10100;
const int M=100100;
int n,m,s,t;
struct node
{
	int v,nxt,w;
}edge[M<<1];
int head[N],cnt=0;
void add(int u,int v,int w)
{
	edge[cnt].v=v;
	edge[cnt].w=w;
	edge[cnt].nxt=head[u];
	head[u]=cnt++;
}
int ans=0;
queue <int> q;
int dis[N];
bool bfs()
{
	while(!q.empty()) q.pop();
	memset(dis,inf,sizeof(dis));
	dis[s]=0;
	q.push(s);
	while(!q.empty())
	{
		int u=q.front();q.pop();
		for(int i=head[u];i!=-1;i=edge[i].nxt)
		{
			int v=edge[i].v;int w=edge[i].w;
			if(w>0&&dis[v]==inf)
			{
				dis[v]=dis[u]+1;
				q.push(v);
			}
		}
	}
	if(dis[t]==inf) return false;
	else return true;
}
int dfs(int u,int dist)
{
	if(u==t||!dist) return dist;
	int res=0;
	for(int i=head[u];i!=-1;i=edge[i].nxt)
	{
		int v=edge[i].v;int w=edge[i].w;
		if(w<0||dis[v]!=dis[u]+1) continue;
		int di=dfs(v,min(dist,w));
		dist-=di;
		res+=di;
		edge[i].w-=di;
		edge[i^1].w+=di;
		if(dist<0) break;
	}
	if(res<0) dis[u]=-1;
	return res;
}
void dinic()
{
	while(bfs()) ans+=dfs(s,inf);
}
int main()
{
	n=read();m=read();s=read();t=read();
	for(int i=1;i<=n;i++) head[i]=-1;
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read(),w=read();
		add(u,v,w);add(v,u,0);
	}
	dinic();
	cout<<ans<<endl;
	return 0;
}


  1. 最大流最小割定理
    在这里插入图片描述
例题

Description
农夫JOHN为牛们做了很好的食品,但是牛吃饭很挑食. 每一头牛只喜欢吃一些食品和饮料而别的一概不吃.虽然他不一定能把所有牛喂饱,他还是想让尽可能多的牛吃到他们喜欢的食品和饮料. 农夫JOHN做了F (1 <= F <= 100) 种食品并准备了D (1 <= D <= 100) 种饮料. 他的N (1 <= N <= 100)头牛都以决定了是否愿意吃某种食物和喝某种饮料. 农夫JOHN想给每一头牛一种食品和一种饮料,使得尽可能多的牛得到喜欢的食物和饮料. 每一件食物和饮料只能由一头牛来用. 例如如果食物2被一头牛吃掉了,没有别的牛能吃食物2.

Input

  • 第一行: 三个数: N, F, 和 D

  • 第2…N+1行: 每一行由两个数开始F_i 和 D_i, 分别是第i 头牛可以吃的食品数和可以喝的饮料数.下F_i个整数是第i头牛可以吃的食品号,再下面的D_i个整数是第i头牛可以喝的饮料号码.

Output

  • 第一行: 一个整数,最多可以喂饱的牛数.

Sample Input
4 3 3

2 2 1 2 3 1

2 2 2 3 1 2

2 2 1 3 1 2

2 1 1 3 3

输入解释:

牛 1: 食品从 {1,2}, 饮料从 {1,2} 中选

牛 2: 食品从 {2,3}, 饮料从 {1,2} 中选

牛 3: 食品从 {1,3}, 饮料从 {1,2} 中选

牛 4: 食品从 {1,3}, 饮料从 {3} 中选

Sample Output
3

输出解释:

一个方案是:

Cow 1: 不吃

Cow 2: 食品 #2, 饮料 #2

Cow 3: 食品 #1, 饮料 #1

Cow 4: 食品 #3, 饮料 #3

在这里插入图片描述
思路

  1. 最朴素的想法是源点连饮料,饮料连牛,牛再连食物,但这样有可能出现一头牛吃喝很多东西
  2. 所以把一头牛拆成两个点,流量为一
  3. 跑dinic

code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstdlib>
#include<ctime>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
inline int read(){
	char ch=' ';int f=1;int x=0;
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
const int N=1e5+100;
struct node
{
	int v,nxt,w;
}edge[N<<1];
int head[N],cnt=0;
void add(int u,int v,int w)
{
	edge[cnt].v=v;
	edge[cnt].w=w;
	edge[cnt].nxt=head[u];
	head[u]=cnt++;
}
int s,t,ans=0;
queue <int> q;
int dis[N];
bool bfs()
{
	memset(dis,inf,sizeof(dis));
	while(!q.empty()) q.pop();
	dis[s]=0;
	q.push(s);
	while(!q.empty())
	{
		int u=q.front();q.pop();
		for(int i=head[u];i!=-1;i=edge[i].nxt)
		{
			int v=edge[i].v;
			int w=edge[i].w;
			if(w>0&&dis[v]==inf)
			{
				dis[v]=dis[u]+1;
				q.push(v);
			}
		}
	}
	if(dis[t]==inf) return false;
	else return true;
}
int dfs(int u,int dist)
{
	if(u==t||!dist) return dist;
	int res=0;
	for(int i=head[u];i!=-1;i=edge[i].nxt)
	{
		int v=edge[i].v;
		int w=edge[i].w;
		if(dis[v]!=dis[u]+1||w<0) continue;
		int di=dfs(v,min(dist,w));
		dist-=di;
		res+=di;
		edge[i].w-=di;
		edge[i^1].w+=di;
		if(!dist) break;
	}
	if(!res) dis[u]=-1;
	return res;
}
void dinic()
{
	while(bfs()) ans+=dfs(s,inf);
}
int main()
{
	int n,f,d;
	n=read();f=read();d=read();
	s=0;t=f+d+n<<1 +1;
	int i,j;
	for(i=0;i<=t;i++) head[i]=-1;
	for(i=1;i<=n;i++)
	{
		int ff,dd;ff=read();dd=read();
		while(ff--){int fi=read();add(fi,f+i,1);add(f+i,fi,0);}
		while(dd--){int di=read();add(f+n+i,f+(n<<1)+di,1);
		add(f+(n<<1)+di,f+n+i,0);}
	}
	for(i=1;i<=f;i++)
	{
		add(s,i,1);add(i,s,0);
	}
	for(i=1;i<=n;i++)
	{
		add(f+i,f+n+i,1);add(f+n+i,f+i,0);
	}
	for(i=1;i<=d;i++)
	{
		add(f+(n<<1)+i,t,1);add(t,f+(n<<1)+i,0);
	} 
	dinic();
	cout<<ans<<endl;
	return 0;
}


经典最小割模型-最大权闭合子图

  1. 问题描述
    在这里插入图片描述
  2. solution
    正权点点权之和-最大流在这里插入图片描述
    https://blog.csdn.net/can919/article/details/77603353

模板题

https://www.luogu.org/problemnew/show/P4174

思路

3. 客户相当于正权点,中转站相当于负权点。要得到一个客户的值,必须要把两个中转站全建好,这恰好符合最大权闭合子图。
  1. 所以我们S向客户连以C为权的边,客户和中转站连inf的边,中转站和T连花费即可
  2. 跑dinic
  3. 注意!!!!一定要把head赋为-1!!!!!
code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int inf=0x3f3f3f3f;
inline int read() {
	int x=0,f=1;char ch=' ';
	while(ch<'0'||ch>'9'){	if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){	x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int N=1e5+100;
const int M=1e6+100;
int s,t,ans=0;
struct node
{
	int v,nxt,w;
}edge[M];
int head[N],cnt=0;
void add(int u,int v,int w)
{
	edge[cnt].v=v;
	edge[cnt].w=w;
	edge[cnt].nxt=head[u];
	head[u]=cnt++;
}
void insert(int u,int v,int w)
{
	add(u,v,w);add(v,u,0);
}

queue <int> q;
int dis[N];
bool bfs()
{
	while(!q.empty()) q.pop();
	memset(dis,inf,sizeof(dis));
	dis[s]=0;
	q.push(s);
	while(!q.empty())
	{
		int u=q.front();q.pop();
		for(int i=head[u];i!=-1;i=edge[i].nxt)
		{
			int v=edge[i].v;int w=edge[i].w;
			if(w>0&&dis[v]==inf)
			{
				dis[v]=dis[u]+1;
				q.push(v);
			}
		}
	}
	if(dis[t]==inf) return false;
	else return true;
}
int dfs(int u,int dist)
{
	if(u==t||!dist) return dist;
	int res=0;
	for(int i=head[u];i!=-1;i=edge[i].nxt)
	{
		int v=edge[i].v;int w=edge[i].w;
		if(w<0||dis[v]!=dis[u]+1) continue;
		int di=dfs(v,min(dist,w));
		dist-=di;
		res+=di;
		edge[i].w-=di;
		edge[i^1].w+=di;
		if(dist<0) break;
	}
	if(res<0) dis[u]=-1;
	return res;
}
void dinic()
{
	while(bfs()) ans-=dfs(s,inf);
}
int main()
{
	int n,m;
	n=read();m=read();
	s=0;t=n+m+1;
	for(int i=0;i<=t;i++) head[i]=-1;
	for(int i=1;i<=n;i++)
	{
		int w=read();
		insert(m+i,t,w);
	}
	for(int i=1;i<=m;i++)
	{
		int a=read(),b=read(),c=read();
		ans+=c;
		insert(i,a+m,inf);insert(i,b+m,inf);
		insert(s,i,c);
	}
	dinic();
	cout<<ans<<endl;
	return 0;
}

在这里插入图片描述

二分图最小点权覆盖

从x或者y集合中选取一些点,使这些点覆盖所有的边,并且选出来的点的权值尽可能小。

建模:

原二分图中的边(u,v)替换为容量为INF的有向边(u,v),设立源点s和汇点t,将s和x集合中的点相连,容量为该点的权值;将y中的点同t相连,容量为该点的权值。在新图上求最大流,最大流量即为最小点权覆盖的权值和。

在这里插入图片描述

例题

在这里插入图片描述

solution

  1. 把一个点拆成两个点,A,B,原图上的边相当于Au->Bv,问题转换成二分图上最小点权覆盖问题
  2. 最大流

二分图最大点权独立集

在二分图中找到权值和最大的点集,使得它们之间两两没有边。其实它是最小点权覆盖的对偶问题。答案=总权值-最小点覆盖集
在这里插入图片描述

例题

https://www.luogu.org/problemnew/show/P2774

solution

  1. 把点黑白染色,选出来的点两两不相邻,相当于黑白二分图,最大独立集
    在这里插入图片描述
    建图
    在这里插入图片描述
    然后点权和-最小割,跑最大点权独立集

code

#include<queue>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
inline int read() {
	int x=0,f=1;char ch=' ';
	while(ch<'0'||ch>'9'){	if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){	x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int maxn=110;
int a[maxn][maxn];
const int N=1e5+100;
const int M=1e6+1000;
int X[4]={1,0,-1,0};
int Y[4]={0,1,0,-1};
struct node
{
	int v,nxt,w;
}edge[M];
int head[N],cnt;
int dis[N];
int s,t;
void add(int u,int v,int w)
{
	edge[cnt].v=v;
	edge[cnt].w=w;
	edge[cnt].nxt=head[u];
	head[u]=cnt++;
}
void insert(int u,int v,int w)
{
	add(u,v,w);add(v,u,0);
}
queue <int> q;
bool bfs()
{
	memset(dis,inf,sizeof(dis));
	while(!q.empty()) q.pop();
	dis[s]=0;
	q.push(s);
	while(!q.empty())
	{
		int u=q.front();q.pop();
		for(int i=head[u];i!=-1;i=edge[i].nxt)
		{
			int v=edge[i].v;
			int w=edge[i].w;
			if(w>0&&dis[v]==inf)
			{
				dis[v]=dis[u]+1;
				q.push(v);
			}
		}
	}
	if(dis[t]==inf) return false;
	else return true;
}
int dfs(int u,int dist)
{
	if(u==t||!dist) return dist;int res=0;
	for(int i=head[u];i!=-1;i=edge[i].nxt)
	{
		int v=edge[i].v;
		int w=edge[i].w;
		if(w<0||dis[v]!=dis[u]+1) continue;
		int di=dfs(v,min(dist,w));
		dist-=di;
		res+=di;
		edge[i].w-=di;
		edge[i^1].w+=di;
		if(dist<=0) break;
	}
	if(res<=0) dis[u]=-1;
	return res;
}
int dinic()
{
	int res=0;
	while(bfs()) res+=dfs(s,inf);
	return res;
}
int main()
{
	int n,m;
	n=read();m=read();
	s=0;t=n*m+1;
	int i,j;
	int ans=0;
	for(i=0;i<=t;i++) head[i]=-1;
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++)
		{
			int w=read();ans+=w;
			if((i+j)%2) insert(s,(i-1)*m+j,w);
			else insert((i-1)*m+j,t,w);
		}
	}
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++)
		{
			if((i+j)%2)
			{
				for(int k=0;k<=3;k++)
				{
					int x=i+X[k],y=j+Y[k];
					if(x>0&&x<=n&&y>0&&y<=m)
					{
						insert((i-1)*m+j,(x-1)*m+y,inf);
					}
				}
			}
		}
	}
	ans-=dinic();
	cout<<ans<<endl;
	return 0;
}

费用流

https://www.luogu.org/problemnew/show/P3381

方法

  1. 使用类似dinic的方法,不过把bfs的时候换成spfa,每次只找一条最短路增广路进行增广,并在spfa的时候记录下本条增广路的路径。
  2. 正确性,首先spfa保证了最短路,直到没有增广路才停止保证了最大流
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstdlib>
#include<ctime>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
inline int read(){
	char ch=' ';int f=1;int x=0;
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
const int N=5100;
const int M=51000;
struct node
{
	int v,nxt,flow,cost;
}edge[M<<1];
int head[N],cnt=0;
void add(int u,int v,int flow,int cost)
{
	edge[cnt].v=v;
	edge[cnt].flow=flow;
	edge[cnt].cost=cost;
	edge[cnt].nxt=head[u];
	head[u]=cnt++; 
}
void insert(int u,int v,int flow,int cost)
{
	add(u,v,flow,cost);
	add(v,u,0,-cost);
}
int maxflow=0,mincost=0;
queue <int> q;
bool vis[N];
int dis[N],flow[N];//dis最短路,flow记录最大流
int pre[N],last[N];// pre[i]表示i的前继节点,last[i]表示他们之间边的编号
int n,m,s,t;
bool spfa()
{
	memset(dis,inf,sizeof(dis));
	memset(flow,inf,sizeof(flow));
	memset(vis,0,sizeof(vis));
	while(!q.empty()) q.pop();
	q.push(s);
	vis[s]=true;dis[s]=0;pre[t]=-1;
	while(!q.empty())
	{
		int u=q.front();q.pop();vis[u]=false;
		for(int i=head[u];i!=-1;i=edge[i].nxt)// i!=-1   !!!!!
		{
			int v=edge[i].v;
			int cost=edge[i].cost;
			if(edge[i].flow>0&&dis[u]+cost<dis[v])
			{
				dis[v]=dis[u]+cost;
				flow[v]=min(flow[u],edge[i].flow);
				pre[v]=u;
				last[v]=i;
				if(!vis[v])
				{
					vis[v]=true;
					q.push(v);
				}
			}
		}
	}
	if(pre[t]==-1) return false;
	else return true;
}
void dinic()
{
	while(spfa())
	{
		int u=t;
		maxflow+=flow[t];
		mincost+=flow[t]*dis[t];
		while(u!=s)
		{
			edge[last[u]].flow-=flow[t];
			edge[last[u]^1].flow+=flow[t];
			u=pre[u];
		}
	}
}
int main()
{

	n=read();m=read();s=read();t=read();
	int i,j;
	for(i=1;i<=n;i++) head[i]=-1;
	for(i=1;i<=m;i++)
	{
		int u=read(),v=read(),flow=read(),cost=read();
		insert(u,v,flow,cost);
	}
	dinic();
	cout<<maxflow<<" "<<mincost<<endl;
	return 0;
}


例题

https://www.luogu.org/problemnew/show/P2153

  1. 题意
    在这里插入图片描述

solution

  1. 首先源点就是1,汇点就是n,如果没有“不经过相同的点”这个条件,那么就是把点与点连上容量为1、费用为权值的边,然后跑最小费用最大流。
  2. 可是还要保证“不经过相同的点”,我们可以参考牛喝饮料的那道题,把一个点拆成两个点,连一条容量为1,费用为0的边。
  3. 注意源点和汇点的细节!!
  4. 注意N和M!!!
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstdlib>
#include<ctime>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=1010;
const int M=100100;

inline int read(){
	char ch=' ';int f=1;int x=0;
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
int s,t;
int maxflow=0,mincost=0;
struct node
{
	int v,nxt,flow,cost;
}edge[M<<1];
int head[N],cnt;
void add(int u,int v,int flow,int cost)
{
	edge[cnt].v=v;
	edge[cnt].flow=flow;
	edge[cnt].cost=cost;
	edge[cnt].nxt=head[u];
	head[u]=cnt++;
}
void insert(int u,int v,int flow,int cost)
{
	add(u,v,flow,cost);
	add(v,u,0,-cost);
}
int dis[N],flow[N];
int pre[N],last[N];
bool vis[N];
queue <int> q;
bool spfa()
{
	memset(dis,inf,sizeof(dis));
	memset(flow,inf,sizeof(flow));
	memset(vis,0,sizeof(vis));
	while(!q.empty()) q.pop();
 	dis[s]=0;vis[s]=true;q.push(s);
	while(!q.empty())
	{
		int u=q.front();q.pop();
		vis[u]=false;
		for(int i=head[u];i!=-1;i=edge[i].nxt)
		{
			int v=edge[i].v;
			if(edge[i].flow>0&&dis[u]+edge[i].cost<dis[v])
			{
				dis[v]=dis[u]+edge[i].cost;
				flow[v]=min(flow[u],edge[i].flow);
				pre[v]=u;
				last[v]=i;
				if(!vis[v])
				{
					vis[v]=true;
					q.push(v);
				}
			}
		}
	}
	if(dis[t]==inf) return false;
	else return true;
}
void dinic()
{
	while(spfa())
	{
		int u=t;
		maxflow+=flow[t];//直接写++也可以
		mincost+=flow[t]*dis[t];
		while(u!=s)
		{
			edge[last[u]].flow-=flow[t];
			edge[last[u]^1].flow+=flow[t];
			u=pre[u];
		}
	}
}
int main()
{
	int n,m;
	n=read();m=read();
	s=1;t=n*2;
	for(int i=1;i<=t;i++) head[i]=-1;
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read(),w=read();
		insert(u+n,v,1,w);
	}
	for(int i=2;i<n;i++) insert(i,i+n,1,0);
	insert(1,n+1,inf,0);
	insert(n,n+n,inf,0);
	dinic();
	cout<<maxflow<<" "<<mincost<<endl;
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java是一种广泛使用的编程语言,有简单、面向对象、跨平台等特点。下面是Java学习的一些重要知识点和学习笔记: 1. Java基础: - 数据类型:Java提供了多种数据类型,包括基本数据类型和引用数据类型。 - 控制程:学习如何使用条件语句(if-else、switch)、循环语句(for、while)等控制程序的程。 - 数组:了解如何声明、初始化和操作数组。 - 方法:学习如何定义和调用方法,以及方法的参数和返回值。 - 类和对象:理解类和对象的概念,学习如何定义类、创建对象和使用类的成员变量和方法。 - 继承和多态:掌握继承和多态的概念,了解如何使用继承创建子类,并实现方法的重写和多态的应用。 2. 面向对象编程: - 封装:学习如何使用访问修饰符(public、private、protected)来控制类的成员的访问权限。 - 继承:了解继承的概念和作用,学习如何使用extends关键字创建子类。 - 多态:理解多态的概念和实现方式,学习如何使用父类引用指向子类对象,实现方法的动态绑定。 3. 异常处理: - 异常的分类:了解异常的分类(Checked Exception和Unchecked Exception)和常见的异常类型。 - 异常处理机制:学习如何使用try-catch语句捕获和处理异常,以及使用throws关键字声明方法可能抛出的异常。 4. 输入输出: - 文件操作:学习如何读写文件,包括使用File类、字节和字符等。 - 序列化:了解对象的序列化和反序列化,学习如何将对象保存到文件或网络中。 5. 集合框架: - 学习Java提供的集合框架,包括List、Set、Map等常用的集合类,以及它们的特点和用法。 6. 多线程编程: - 学习如何创建和管理线程,了解线程同步和线程间通信的方法。 7. 数据库连接: - 学习如何使用Java连接数据库,执行SQL语句,进行数据的增删改查操作。 以上是Java学习的一些重要知识点和学习笔记,希望对你有帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值