zoukankan      html  css  js  c++  java
  • 师徒恋大法好

    原为PG所作,然而由于一些不可告人的原因,文章放到这儿了……

    有了STL,不必再从头写大多的标准数据结构和算法,并且可获得非常高的性能。(如果没有氧气,最好不要用vector,deque,set,multiset,map,string)。

    废话不多说,让我们一起看看STL到底有多好用。

    1.vector

    可变长度的数组。(可节省空间)

    常用操作:

    #include<vector>
    vector<int>a;
    a[i];//重载了[],返回数组的第i+1个元素(从0开始)
    a.empty()//数组为空返回1,否则返回0
    a.size()//返回数组元素个数
    a.push_back(x)//尾部插入元素x
    a.pop_back()//删除尾部元素
    a.begin()//返回第一个元素迭代器
    a.end()//返回最后一个元素迭代器
    

    上面的操作时间复杂度均为O(1);(但常数大,比数组慢,只有吸氧才能和数组媲美)

    下面的操作慎用:

    a.erase(it1,it2)//删除迭代器it1到it2的元素(前闭后开)
    a.erase(it)//删除迭代器it指向的元素
    a.clear();//清空数组
    a.insert(it,x)//在迭代器为it的位置加入x,其他后移
    a.assign(b.begin(),b.end())//把vector b复制到vector a
    

    这些操作时间复杂度为O(n),且常数较大。

    vector支持algorithm库中的某些操作,如:

    sort(a.begin(),a.end(),cmp);//排序,cmp是自定义函数。
    lower_bound(a.begin(),a.end(),x);//返回数组中第一个大于等于x的元素的迭代器。
    upper_bound(a.begin(),a.end(),x)//返回数组中第一个大于x的元素的迭代器。
    max_element(a.begin(),a.end())//返回数组最大值
    min_element(a.begin(),a.end())//返回数组最小值
    random_shuffle(a.begin(),a.end())//随机打乱,在随机化(乱搞)算法中用到。
    reverse(a.begin(),a.end())//数组反转
    

    注意:没有氧气,慎用vector

    下面通过几道例题感受vector的简便。

    例题1:洛谷P1177【模板】快速排序

    虽然可以用sort,但我们尝试使用vector来实现。

    #include<bits/stdc++.h>
    std::vector<int>a;
    int main(){
    	int n,x;
    	scanf("%d",&n);
    	for(register int i=0;i<n;++i)scanf("%d",&x),a.insert(upper_bound(a.begin(),a.end(),x),x);
    	for(register int i=0;i<n;++i)printf("%d ",a[i]);
    	return 0;
    }
    

    从上面可以看出,vector可以非常便捷地支持插排。

    例题2:P3378 【模板】堆

    虽然可以使用另一个容器priority_queue,但我们可以使用vector切了它。

    #include<bits/stdc++.h>
    std::vector<int>a;
    int main(){
    	int n,k,x;
    	scanf("%d",&n);
    	for(register int i=1;i<=n;++i){
    		scanf("%d",&k);
    		if(k==1){
    			scanf("%d",&x);
    			a.insert(upper_bound(a.begin(),a.end(),x),x);
    		}
    		else if(k==2)printf("%d
    ",a[0]);
    		else a.erase(a.begin());
    	}
    	return 0;
    }
    

    从上面两道例题可以看出,vector的操作也不一定慢(玄学n方过百万),但是最好注意程序的常数,能开O2尽量开

    例题3:洛谷P3369 【模板】普通平衡树

    此题是上述操作的综合,不要以为数组很菜,它能支(A)持(K)平(I)衡(O)树(I)的操作呢。(骚的一批)

    #include<bits/stdc++.h>
    std::vector<int>a;
    int main(){
    	int n,opt,x;
    	scanf("%d",&n);
    	for(register int i=1;i<=n;++i){
    		scanf("%d%d",&opt,&x);
    		if(opt==1)a.insert(upper_bound(a.begin(),a.end(),x),x);
    		else if(opt==2)a.erase(lower_bound(a.begin(),a.end(),x));
    		else if(opt==3)printf("%d
    ",lower_bound(a.begin(),a.end(),x)-a.begin()+1);
    		else if(opt==4)printf("%d
    ",a[x-1]);
    		else if(opt==5)printf("%d
    ",*(lower_bound(a.begin(),a.end(),x)-1));
    		else printf("%d
    ",*(upper_bound(a.begin(),a.end(),x)));
    	}
    	return 0;
    }
    

    从第一个容器就能看出师徒恋的方便了吧

    2.stack

    常用操作:

    #include<stack>
    stack<int>st;
    st.push(x)//向栈顶加入x,O(1)
    st.pop()//栈顶出栈,O(1)
    st.top()//返回栈顶,O(1)
    

    看出来了吗?stack的所有操作vector都能支持。

    但栈的思想很重要,后缀表达式,tarjan强连通分量算法以及单调栈等都需要用到。

    这里给大家推荐一道单调栈例题,感受一下单调栈

    例题:洛谷SP1805 HISTOGRA - Largest Rectangle in a Histogram

    如果矩形从左到右单调递增,答案是以每个矩形的高度为高度,从当前矩形到右边界为宽度的矩形的面积的最大值

    如果下一个比上一个矮,那么这块矩形和之前的矩形构成较大面积时,新矩形高度不可能超过此矩形高度,所以可以把比此矩形高的矩形删掉,用宽度不变,高度为此矩形高度的矩形替代

    简单说,我们维护一个栈,保存高度单调递增的矩形,然后扫描每个矩形,如果比栈顶矩形高,进栈,否则栈顶出栈直至栈空或栈顶矩形比当前矩形矮(简单吧)

    #include<bits/stdc++.h>
    long long n,p,a[100001],w[100001],ans,kd;
    std::stack<long long>st;
    int main(){
    	while(scanf("%lld",&n)&&n){
    		for(register long long i=1;i<=n;++i)scanf("%lld",&a[i]);
    		a[n+1]=0;st.push(0);ans=0;//注意栈为空时.top()会出错,a[n+1]=0避免结束后栈中还有矩形
    		for(register long long i=1;i<=n+1;++i)
    			if(a[i]>st.top())st.push(a[i]),w[st.size()]=1;//比栈顶矩形高
    			else{//否则更新答案
    				kd=0;
    				while(st.top()>a[i])kd+=w[st.size()],ans=std::max(ans,kd*st.top()),st.pop();
    				st.push(a[i]);w[st.size()]=kd+1;
    			}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    

    这就是单调栈,O(n),借助单调性,能及时排除不可能选项,保持高度有效性和秩序性。

    3.queue

    循环队列(可节省空间)

    常用操作:

    #include<queue>
    queue<int>q;
    q.push(x)//队尾加入x
    q.pop()//队首出队
    q.front()//返回队首
    q.back()//返回队尾
    q.empty()//队列为空返回1否则返回0
    q.size()//返回队列大小
    

    循环队列的操作都是O(1)的,并且常数较小,使用方便。

    循环队列一般用于广搜,树和图的广度优先遍历,拓扑排序和SPFA等算法中。

    树和图的广度优先遍历:

    inline void bfs(){
    	memset(d,0,sizeof(d));//d数组记录点在树中的深度或点在图中的层次。
    	queue<int>q;
    	q.push(1);d[1]=1;
    	whiel(!q.empty()){
    		int x=q.front();q.pop();
    		for(register int i=head[x];i;i=edge[i].next){//链式前向星存边
    			if(d[edge[i].to])continue;
    			d[edge[i].to]=d[x]+1;q.push(edge[i].to);
    		}
    	}
    }
    

    广搜在骗分博客中会详讲。此处略

    拓扑排序:

    #include<bits/stdc++.h>
    int x,n,m,deg[1000001],head[1000001],u,v,a[1000001],cnt;
    struct Edge{int next,to;}edge[1000001];
    inline void topsort(){
    	queue<int>q;
    	for(register int i=1;i<=n;++i)if(!deg[i])q.push(i);
    	while(!q.empty()){
    		int x=q.front();q.pop();a[++cnt]=x;
    		for(register int i=head[x];i;i=edge[i].next)if(--deg[edge[i].to])q.push(edge[i].to);
    	}
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(register int i=1;i<=m;++i){scanf("%d%d",&u,&v);edge[i].next=head[u];edge[i].to=v;head[u]=i;++deg[v];}
    	//链式前向星存边并统计入度
    	topsort();
    	for(register int i=1;i<=cnt;++i)printf("%d ",a[i]);
    	return 0;
    }
    

    洛谷P3371 【模板】单源最短路径(弱化版)

    关于SPFA 它死了

    #include<bits/stdc++.h>
    int n,m,k,x,head[1000001],dis[1000001],vis[1000001],u,v,w;
    struct Edge{int next,to,w;}edge[1000001];
    inline void spfa(int k){
    	for(register int i=1;i<=n;++i)dis[i]=0x7fffffff,vis[i]=0;
    	std::queue<int>q;q.push(k);dis[k]=0;vis[k]=1;
    	while(!q.empty()){
    		x=q.front();q.pop();vis[x]=0;
    		for(register int i=head[x];i;i=edge[i].next)
    			if(dis[edge[i].to]>dis[x]+edge[i].w){
    				dis[edge[i].to]=dis[x]+edge[i].w;
    				if(!vis[edge[i].to])vis[edge[i].to]=1,q.push(edge[i].to);
    			}
    	}
    }
    int main(){
    	scanf("%d%d%d",&n,&m,&k);
    	for(register int i=1;i<=m;++i){scanf("%d%d%d",&u,&v,&w);edge[i].next=head[u];edge[i].to=v;edge[i].w=w;head[u]=i;}
    	spfa(k);
    	for(register int i=1;i<=n;++i)printf("%d ",dis[i]);
    	return 0;
    }
    

    4.deque

    双端队列deque==vector+queue,即能高效从队列两端进行操作的队列,包含所有vector支持的操作,还支持:

    #include<deque>
    deque<int>dq;
    dq.push_front(x)//从队首插入,O(1)
    dq.pop_front()//队首出队,O(1)
    

    有了vector和queue,为什么还要deque呢?

    因为有单调栈就要有单调队列啦(雾

    双端队列可以支持单调队列的操作

    例题:洛谷 P1886 滑动窗口

    用两个双端队列维护区间最值

    #include<bits/stdc++.h>
    int x,n,m,cnt=1,k;
    std::deque<std::pair<int,int> >q,q1;//q维护单调递减序列,q1维护单调递增序列
    int ans[2][1000001];
    int main(){
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++){
            scanf("%d",&x);
            while(!q.empty()&&x>=q.back().second)q.pop_back();//保持单调性
            while(!q1.empty()&&x<=q1.back().second)q1.pop_back();
            q.push_back(std::make_pair(i,x));q1.push_back(std::make_pair(i,x));
            while(i-k>=q.front().first)q.pop_front();//保证序列在区间内
            while(i-k>=q1.front().first)q1.pop_front();
            if(i>=k)ans[0][cnt]=q.front().second,ans[1][cnt++]=q1.front().second;
        }
        for(int i=1;i<cnt;i++)printf("%d ",ans[1][i]);
        puts("");
        for(int i=1;i<cnt;i++)printf("%d ",ans[0][i]);
        return 0;
    }
    

    单调栈和单调队列都是挖掘题目中的单调性,及时排除不可能,能保持序列的高度有效性和秩序性,能解决一些高级数据结构(如线段树)才能解决的问题,值得我们思考

    注意,deque和vector比较相似,最好降低常数,没有氧气,慎用deque

    因为NOIP所以不讲单调队列优化多重背包

    5.pair

    这就是上面代码中出现的pair(没什么好讲的

    二元组,尖括号内为自己定义的类型,用.first()访问第一元,用.second()访问第二元,重载了<,第一元优先级高于第二元

    主要用于STL其他容器的存储类型,如上面程序中双端队列的类型为一个二元组,第一元表示序号,第二元表示大小

    6.priority_queue

    优先队列,通俗讲就是没素质,插队

    操作:

    #include<queue>
    priority_queue<int>q;//定义大根堆,即大的插队
    priority_queue<int,vector<int>,greater<int> >q;//定义小根堆,即小的插队
    q.push(x)//将x插入堆,O(logn)
    q.pop()//堆顶出堆,O(logn)
    q.top()//返回堆顶,O(1)
    

    看完上面的操作,你想到了什么呢?对,没错,它能动态维护序列的最值

    堆能优化贪心,dijkstra,prim等算法

    但需要注意优先队列存储类型必须重载<。优先队列不支持erase,这让我们很蛋疼,解决方案为在删除时,在堆外记录(例如记录元素最新值),用于鉴别过时元素,在堆顶取出最值时,再检查是不是过时的,若是,取下一个

    例题1:洛谷 P1090 合并果子

    思路就是每次取出两个最小值合并,用堆维护

    #include<bits/stdc++.h>
    int n,x,ans,a,b;
    std::priority_queue<int,std::vector<int>,std::greater<int> >q;
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&x),q.push(x);
        while(q.size()>=2)a=q.top(),q.pop(),b=q.top(),q.pop(),ans+=a+b,q.push(a+b);
        printf("%d
    ",ans);
        return 0;
    }
    

    这就是堆优化贪心

    例题2:洛谷P4779 【模板】单源最短路径(标准版)

    堆优化dijkstra

    #include<bits/stdc++.h>
    int x,y,n,m,k,u,v,w,head[1000001],dis[1000001],vis[1000001];
    struct Edge{int next,to,w;}edge[1000001];
    inline void dijkstra(int k){
    	for(register int i=1;i<=n;++i)dis[i]=0x7fffffff,vis[i]=0;//初始化
    	std::priority_queue<std::pair<int,int>,std::vector<std::pair<int,int> >,std::greater<std::pair<int,int> > >q;q.push(std::make_pair(0,k));dis[k]=0;
    	while(!q.empty()){
    		x=q.top().second;q.pop();
    		if(vis[x])continue;
    		vis[x]=1;
    		for(register int i=head[x];i;i=edge[i].next)if(dis[edge[i].to]>dis[x]+edge[i].w)dis[edge[i].to]=dis[x]+edge[i].w,q.push(std::make_pair(dis[edge[i].to],edge[i].to));
    	}
    }
    int main(){
    	scanf("%d%d%d",&n,&m,&k);
    	for(register int i=1;i<=m;++i)scanf("%d%d%d",&u,&v,&w),edge[i].next=head[u],edge[i].to=v,edge[i].w=w,head[u]=i;
    	dijkstra(k);
    	for(register int i=1;i<=n;++i)printf("%d ",dis[i]);
    	return 0;
    }
    

    毒瘤压行

    这就是堆优化dijkstra,O((n+m)logn)比某个死了的算法好多了

    7.string

    字符串string,很多人不是很会使用它,其实它能在水掉很多字符串的题

    基本操作:

    #include<string>
    #include<sstream>//stringstream头文件
    string s,s1;
    stringstream ss(s);
    ss>>s;
    char c;
    cin>>s//输入
    [],+,+=,>,<,>=,<=,==,!=,= //重载[],从0开始编号,+连接两个字符串,比较运算符从第一个字符开始比较,=赋值
    getline(cin,s)//输入一行
    s.push_back(c)//字符串尾端加入字符c
    s.append(c)//字符串尾端加入字符c
    s.append(n,c)//在字符串尾端加入n个字符c
    s.insert(it,c)//在迭代器it处插入c
    s.substr(l,r)//返回l到r的字符串(前闭后开)
    s.find(s1)//搜索字符串,返回第一次找到的位置,若没找到返回-1
    s.empty()//是否为空,为空返回1,否则返回0
    s.erase(it)//清除迭代器it指向的元素
    s.erase(it1,it2)//清除迭代器it1到it2之间的元素(前闭后开)
    s.replace(a,len,str)//从a开始len个用str替换
    

    常用操作就是这些了,find在一定程度上可以替代KMP,+和substr可以构造字符串只要不是模板题或数据卡你,就算时间复杂度是错的,也能在你不会写时骗到更多的分,且调试难度极低,纯模拟至少能拿到分,只要你坚信,n^2过百万,暴力碾标算,根据常数的优秀以及NOIP的水数据就有可能得到AC的好成绩

    你肯定会好奇stringstream是什么,让我来告诉你

    试想一道题因为找不到这样的题,输入若干个整数,求和

    如果你会快读,应该能写出来,但是容易写错,也有点难理解,这里提供一种解决方案,使用stringstream,很容易理解

    #include<bits/stdc++.h>
    string s;
    int sum,x;
    int main(){
    	getline(std::cin,s);
    	stringstream ss(s);
    	while(ss>>x)sum+=x;
    	printf("%d",sum);
    	return 0;
    }
    

    现在理解stringstream了吧,它能将读入的字符串变成其他类型的变量,或一行字符串变成空格分隔的多个字符串。但请注意,string很慢,stringstream更慢,所以还是那句话,没有氧气,慎用

    综上,有效地利用各种字符串处理函数和模拟,能使你的NOIP分数提高一个档次

    8.set和multiset

    集合和可重集合,内部实现是一棵红黑树呵呵,但是常数较大使得它的效率较低

    基本操作:

    #include<set>
    set<int>a;
    multiset<int>b;
    a.size(x)//返回大小,O(1)
    a.empty()//返回是否为空,空返回1,否则返回0
    a.clear()//清空,O(nlogn)
    a.begin()//返回最小元素迭代器
    a.end()//返回最大元素迭代器
    a.insert(x)//插入x
    a.find(x)//查找x,若存在返回指向该元素的迭代器,若不存在返回a.end(),O(logn)
    a.lower_bound(x)//返回第一个大于等于x的元素的迭代器,O(logn)
    a.upper_bound(x)//返回第一个大于x的元素的迭代器,O(logn)
    a.erase(it)//删除迭代器it指向的元素,O(logn)
    a.erase(x)//删除所有等于x的元素,O(k+logn),k为等于x的元素个数
    if((it=s.find(x))!=s.end())s.erase(it);//可以只删除一个等于x的元素
    a.count(x)//返回元素个数,O(k+logn),k为等于x的元素个数
    

    set能排序并去重,能方便地进行离散化等操作,还能用它实现珂朵莉树。但注意,set,multiset和map的迭代器的只支持自增或自减且自增或自减的时间复杂度是O(logn)的,所以在使用时一定要注意

    例题1:洛谷 UVA10815 Andy's First Dictionary

    由于string重载了<,所以把非字母字符转化为空格,再用stringstream得到单词丢进set,排序且去重

    #include<bits/stdc++.h>
    std::set<std::string>zd;
    std::string s,dc;
    int main(){
    	while(std::cin>>s){
    		for(register int i=0;i<s.size();++i)
    			if(s[i]>='a'&&s[i]<='z'||s[i]>='A'&&s[i]<='Z')s[i]=tolower(s[i]);
    			else s[i]=' ';
    		std::stringstream ss(s);
    		while(ss>>dc)zd.insert(dc);
    	}
    	for(std::set<std::string>::iterator it=zd.begin();it!=zd.end();++it)std::cout<<*it<<std::endl;
    	return 0;
    }
    

    从上面的例题就能看出set排序加去重的方便了

    例题2:洛谷 UVA136 Ugly Numbers

    用优先队列保存所有已生成的丑数,每次取最小x,生成2x,3x,5x插入堆中,注意一个丑数有多种生成方式,所以可以用set判断是否已生成过

    #include<bits/stdc++.h>
    std::priority_queue<long long,std::vector<long long>,std::greater<long long> >q;
    std::set<long long>s;
    long long a[3]={2,3,5},x,t;
    int main(){
    	q.push(1);s.insert(1);
    	for(register int i=1;;++i){
    		x=q.top();q.pop();
    		if(i==1500){printf("The 1500'th ugly number is %d.
    ",x);break;}
    		for(register int j=0;j<3;++j){
    			t=x*a[j];
    			if(!s.count(t))s.insert(t),q.push(t);
    		}
    	}
    	return 0;
    }
    

    从上可以看出学会同时运用多个容器,融会贯通,能解决更多的题目。

    9.map

    map映射,它的内部实现也是一棵红黑树呵呵,同样,也因为常数较大使得效率较低

    基本操作

    #include<map>
    map<key,value>a;//建立一个key到value的映射,如map<string,int>a
    a.size()//返回大小,O(1)
    a.empty()//返回是否为空,空返回1,否则返回0,O(1)
    a.clear()//清空,O(nlogn)
    a.begin()//返回第一个元素的迭代器,O(1)
    a.end()//返回最后一个元素的迭代器,O(1)
    a.insert(pair<string,int>)//参数必须是pair,O(logn)
    a.erase(pair<string,int>)//参数必须是pair或迭代器,O(logn)
    a.erase(it)//O(logn)
    a.find(x)//查找x,若存在返回指向key为x的二元组的迭代器,O(logn)
    a[x]//重载了[],O(logn),需要注意,若x不存在,则会自动新建一个二元组(x,0或NULL),强烈建议用[]之前,先用find()检查存在性
    

    map可不得了,它可以一定程度上代替hash表

    例题1:洛谷 P3370 【模板】字符串哈希

    用map不光能解决这道题,还能统计单词的出现次数

    #include<bits/stdc++.h>
    std::map<std::string,int>a;
    int n;
    std::string s;
    int main(){
    	scanf("%d",&n);
    	while(n--){
    		std::cin>>s;
    		if(a.find(s)==a.end())++a[s];//统计单词的出现次数
    	}
    	printf("%d",a.size());
    	return 0;
    }
    

    可以方便的代替hash表,但常数大,有时需要注意

    例题2:洛谷 UVA156 Ananagrams

    把每个单词初始化,全部转化成小写在排序,然后丢进map统计

    #include<bits/stdc++.h>
    std::map<std::string,int>mp;
    std::vector<std::string>a;
    std::string s;
    int n;
    inline std::string init(const std::string &s){//初始化
    	std::string ans=s;
    	for(register int i=0;i<ans.size();++i)ans[i]=tolower(ans[i]);
    	sort(ans.begin(),ans.end());
    	return ans;
    }
    int main(){
    	while(std::cin>>s&&s[0]!='#'){
    		a.push_back(s);std::string s1=init(s);
    		if(!mp.count(s1))mp[s1]=0;
    		mp[s1]++;
    	}
    	std::vector<std::string>ans;
    	for(register int i=0;i<a.size();++i)if(mp[init(a[i])]==1)ans.push_back(a[i]);
    	sort(ans.begin(),ans.end());
    	for(register int i=0;i<ans.size();++i)std::cout<<ans[i]<<std::endl;
    	return 0;
    }
    

    此例说明,没有良好的代码设计,是无法发挥STL的威力的,如果没有想到初始化,很难用map简化代码

    10.bitset

    bitset可看做多位二进制数,每8位占用一个字节,相当于状压的bool数组,支持基本位运算,n位bitset执行一次位运算的复杂度可视为n/32,效率较高

    #include<bitset>
    bitset<1000001>s;//尖括号中写位数
    ~s//按位取反
    &,|,^//按位与或,异或
    <<,>> //左移右移
    ==,!= //比较是否相等
    s[k]//表示第k位,从0开始
    s.count()//返回多少位为1,O(n)
    s.any()//若所有位为0返回0,否则返回1
    s.none()//若所有位为0返回1,否则返回0
    s.set()//所有位变为1
    s.set(k,v)//把第k位变为v,即s[k]=v
    s.reset()//把所有位变为0,
    s.reset(k)//把第k为变为0,即s[k]=0
    s.flip()//把所有位取反,即s=~s
    s.flip(k)//把第k位取反,即s[k]^=1
    

    例题

    11.algorithm

    STL的算法库,提供了

    int a[1000001];
    reverse(a+1,a+n+1);//翻转1~n
    int m=unique(a+1,a+n+1)-a-1;//m为去重后的元素个数
    random_shuffle(a+1,a+n+1);//随机打乱
    do{
    }next_permutation(a+1,a+n+1);//下一个排列,当不存在排名更靠后的排列返回0,否则返回1
    sort(a+1,a+n+1);//快排
    inline void cmp(const int &a,const int &b){return ...}
    sort(a+1,a+n+1,cmp)//cmp自定义快排,返回值为1即交换
    lower_bound(a+1,a+n+1,x)//返回第一个大于等于x的元素下标
    upper_bound(a+1,a+n+1,x)//返回第一个大于x的元素下标
    

    上述就是STL库中的常用的函数,其中a数组可用vector,deque,string等容器替换

    最后,祝大家NOIP2018 rp++

  • 相关阅读:
    Linux防火墙使用配置
    es安装笔记
    git仓库免密码登陆配置
    swgger前后端分离api生成
    关于redis
    学习笔记关于springboot
    idea 安装记录
    随记
    开课吧--Python数据分析--第4节 数据七十二变--互动练习:如果你不需要,就让它消失!
    jupyter使用方法
  • 原文地址:https://www.cnblogs.com/wendster/p/stlwin.html
Copyright © 2011-2022 走看看