基础向模板收集(图论篇)

以下部分内容抄自神仙博客

最短路计数

要记录 v i s vis vis数组避免算重。

if(dis[v]>dis[u]+W[i]) dis[v]=dis[u]+W[i],num[v]=num[u],Q.push(mp(-dis[v],v));
else if(dis[v]==dis[u]+W[i]) num[v]+=num[u];

这里必须加 e l s e else else,因为上面那个算完之后下面那个就成立了。
模板

#include<bits/stdc++.h>
#define pii std::pair<int,int>
#define mp std::make_pair
#define fi first
#define se second
#define re register
#define cs const
cs int N=2e3+10,oo=0x3f3f3f3f;
namespace IO{
	cs int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	template <typename T>
	inline T get(){
		char ch=gc();T x=0;
		while(!isdigit(ch)) ch=gc();
		while(isdigit(ch)) x=(x+(x<<2)<<1)+(ch^48),ch=gc();
		return x;
	}
	inline int gi(){return get<int>();}
}
using IO::gi;
int n,m,len[N][N],dis[N],num[N],vis[N];
int Next[N*N/2],V[N*N/2],W[N*N/2],Head[N],cnt=0;
inline void add(int u,int v,int w){Next[++cnt]=Head[u],V[cnt]=v,W[cnt]=w,Head[u]=cnt;}
inline void dijkstra(int S){
	std::priority_queue<pii> Q;
	memset(dis,0x3f,sizeof dis);
	dis[S]=0,num[S]=1,Q.push(mp(0,S));
	while(!Q.empty()){
		int u=Q.top().se;Q.pop();
		if(vis[u]) continue;vis[u]=1;
		for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]]){
			if(dis[v]>dis[u]+W[i]) dis[v]=dis[u]+W[i],num[v]=num[u],Q.push(mp(-dis[v],v));
			else if(dis[v]==dis[u]+W[i]) num[v]+=num[u];
		}
	}
}
int main(){
//	freopen("1608.in","r",stdin);
	n=gi(),m=gi(),memset(len,0x3f,sizeof len);
	while(m--){
		int u=gi(),v=gi(),w=gi();
		if(len[u][v]>w) len[u][v]=w;
	}
	for(int re i=1;i<=n;++i)
		for(int re j=1;j<=n;++j)
			if(len[i][j]!=oo) add(i,j,len[i][j]);
	dijkstra(1),printf((dis[n]==oo)?"No answer":"%d %d\n",dis[n],num[n]);
}

SPFA判负环

若一个点进出队列次数超过 n n n次,则有负环。传送门

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
cs int N=2001,M=6001;
namespace IO{
	cs int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	template<typename T>
	inline T get(){
		char ch=gc();T x=0,f=1;
		while(!isdigit(ch)){if(ch=='-')f=0;ch=gc();}
		while(isdigit(ch)) x=(x+(x<<2)<<1)+(ch^48),ch=gc();
		return f?x:-x;
	}
	inline int gi(){return get<int>();}
	inline ll gl(){return get<ll>();}
}
using IO::gi;
using IO::gl;
int T,n,m,dis[N],vis[N],num[N];
int Next[M],V[M],W[M],Head[N],cnt=0;
inline void add(int u,int v,int w){Next[++cnt]=Head[u],V[cnt]=v,W[cnt]=w,Head[u]=cnt;}
inline void init(){
	memset(Head,0,sizeof Head),cnt=0;
}
inline bool SPFA(int S){
	std::queue<int> Q;
	memset(dis,0x3f,sizeof dis);
	memset(vis,0,sizeof vis);
	Q.push(S),vis[S]=1,dis[S]=0,num[S]=1;
	while(!Q.empty()){
		int u=Q.front();Q.pop(),vis[u]=0;
		for(int i=Head[u],v=V[i];i;v=V[i=Next[i]])
			if(dis[v]>dis[u]+W[i]){
				dis[v]=dis[u]+W[i],num[v]=num[u]+1;
				if(num[v]>n) return true;
				if(!vis[v]) vis[v]=1,Q.push(v);
			}
	}return false;
}
int main(){
//	freopen("3385.in","r",stdin);
	T=gi();
	while(T--){
		n=gi(),m=gi(),init();
		while(m--){
			int u=gi(),v=gi(),w=gi();
			(w<0)?(add(u,v,w)):(add(u,v,w),add(v,u,w));
		}puts((SPFA(1))?("YE5"):("N0"));
	}
}

割点

根节点为割点条件: d f s dfs dfs树上的儿子数大于1
非根节点为割点条件: l o w [ v ] ⩾ d f n [ u ] low[v] \geqslant dfn[u] low[v]dfn[u],即存在一个儿子不能 d f s dfs dfs到当前点的祖先节点。
注意 d f s dfs dfs开始的时候要把 r o o t root root置为开始 d f s dfs dfs的节点。

一道例题:
[HNOI2012]矿场搭建
这个题就是考虑若干个点双集合。
若点双里包含割点个数大等于2,则不用安排。因为只有一个点会爆掉,所以这个点双至少可以和一个其他点双连通。
若点双里只有一个割点,那么出口可以安排在非割点的任意地方。你会发现不管炸割点或者炸出口都可以安排所有人逃离。
若整个点双无割点,则至少安排两个,因为如果炸掉了一个出口,就跑不掉了。

于是求出割点后 d f s dfs dfs一遍找出每个点双对应的答案。
第一个答案就是加法原理,第二个就是乘法原理。

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
cs int N=505,M=505;
namespace IO{
	cs int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	template<typename T>inline T get(){
		char ch=gc();T x=0,f=1;
		while(!isdigit(ch)){if(ch=='-')f=0;ch=gc();}
		while(isdigit(ch)) x=(x+(x<<2)<<1)+(ch^48),ch=gc();
		return f?x:-x;
	}
	inline int gi(){return get<int>();}
	inline ll gl(){return get<ll>();}
}
using IO::gi;
using IO::gl;
inline int min(int x,int y){return (x<y)?x:y;}
inline int max(int x,int y){return (x>y)?x:y;}
inline void Max(int &x,int y){if(x<y)x=y;}
inline void Min(int &x,int y){if(x>y)x=y;}

int n,m,u,v,num_cut,num_pnt,num,Case=0;
int Head[N],Next[M<<1],V[M<<1],cnt;
int col[N],dfn[N],low[N],cut[N],root,tot;
int ans1;ll ans2;

inline void init(){
	n=cnt=tot=num=ans1=0;ans2=1;
	memset(Head,0,sizeof Head);
	memset(dfn,0,sizeof dfn);
}
inline void add(int u,int v){Next[++cnt]=Head[u],V[cnt]=v,Head[u]=cnt;}
inline void tarjan(int u,int son=0){
	cut[u]=col[u]=0,dfn[u]=low[u]=++tot;
	for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]]){
		if(!dfn[v]){
			++son,tarjan(v),Min(low[u],low[v]);
			cut[u]|=(u==root&&son>1)|(u!=root&&low[v]>=dfn[u]);
		}else Min(low[u],dfn[v]);
	}
}
inline void dfs(int u){
	col[u]=num;if(cut[u]) return;
	++num_pnt;
	for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]]){
		if(cut[v]&&col[v]!=num) ++num_cut,col[v]=num;
		if(!col[v]) dfs(v);
	}
}
int main(){
//	freopen("3225.in","r",stdin);
	while(m=gi()){
		init();
		while(m--) u=gi(),v=gi(),Max(n,max(u,v)),add(u,v),add(v,u);
		for(int re i=1;i<=n;++i) if(!dfn[i]) tarjan(root=i);
		for(int re i=1;i<=n;++i) if(!(col[i]+cut[i])){
			++num,num_pnt=num_cut=0,dfs(i);
			if(num_cut==1) ans1+=1,ans2*=num_pnt;
			if(num_cut==0) ans1+=2,ans2*=1ll*num_pnt*(num_pnt-1)/2;
		}printf("Case %d: %d %lld\n",++Case,ans1,ans2);
	}
}

附上点双写法:
注意要先求割点再跑点双,因为要记录点双里有几个割点。
这里使用的是存边的方法。
然而有一个问题:当 d f s dfs dfs到一条非树边时,要判断一下 d f n dfn dfn值大小关系。如果 d f n [ v ] > d f n [ u ] dfn[v]>dfn[u] dfn[v]>dfn[u],就说明它是割顶连向子树的一条边。此时以 u u u为割顶的点双已求出,而如果再把这条边压进栈里,这条边就弹不出去了,它的两个端点就会被计入其他点双。于是就挂了。然后判一个 d f n [ v ] < d f n [ u ] dfn[v]<dfn[u] dfn[v]<dfn[u]就可以过了。也就是说这条边是一条返祖边。

对于这道题而言,贡献与边无关,所以其实并不用记录点双中具体的边的情况,只要保证边能更新到所有点即可,所以返祖边不记也是可以的。

但是对于需要统计边的情况的题,就必须要记录返祖边了。

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
cs int N=505,M=505;
namespace IO{
	cs int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	template<typename T>inline T get(){
		char ch=gc();T x=0,f=1;
		while(!isdigit(ch)){if(ch=='-')f=0;ch=gc();}
		while(isdigit(ch)) x=(x+(x<<2)<<1)+(ch^48),ch=gc();
		return f?x:-x;
	}
	inline int gi(){return get<int>();}
	inline ll gl(){return get<ll>();}
}
using IO::gi;
using IO::gl;
inline int max(int x,int y){return (x>y)?x:y;}
inline int min(int x,int y){return (x<y)?x:y;}
inline void Min(int &x,int y){if(x>y)x=y;}
inline void Max(int &x,int y){if(x<y)x=y;}

int n,m,u,v,Case=0,ans1;ll ans2;
int Head[N],Next[M<<1],V[M<<1],F[M<<1],cnt=1;
int dfn[N],low[N],cut[N],belong[N],st[M<<1],top,root,tot;
int bcc_pnt[N],bcc_cut[N],bcc_cnt;
inline void add(int u,int v){Next[++cnt]=Head[u],V[cnt]=v,F[cnt]=u,Head[u]=cnt;}
inline void init(){
	ans1=0,ans2=1;
	n=tot=cnt=bcc_cnt=top=0;
	memset(Head,0,sizeof Head);
	memset(dfn,0,sizeof dfn);
	memset(cut,0,sizeof cut);
	memset(bcc_pnt,0,sizeof bcc_pnt);
	memset(bcc_cut,0,sizeof bcc_cut);
	memset(belong,0,sizeof belong);
}
inline void clear(){memset(dfn,0,sizeof dfn),tot=0;}
inline void tarjan_cut(int u,int son=0){
	dfn[u]=low[u]=++tot;
	for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]]){
		if(!dfn[v]) ++son,tarjan_cut(v),Min(low[u],low[v]),cut[u]|=(u==root&&son>1)|(u!=root&&low[v]>=dfn[u]);
		else Min(low[u],dfn[v]);
	}
}
inline void tarjan_bcc(int u){
	dfn[u]=low[u]=++tot;
	for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]]){
		if(!dfn[v]){
			st[++top]=i,tarjan_bcc(v),Min(low[u],low[v]);
			if(low[v]>=dfn[u]){
				++bcc_cnt;
				while(1){
					int now=st[top--];
					if(belong[F[now]]!=bcc_cnt) bcc_pnt[bcc_cnt]+=1,bcc_cut[bcc_cnt]+=cut[F[now]],belong[F[now]]=bcc_cnt;
					if(belong[V[now]]!=bcc_cnt) bcc_pnt[bcc_cnt]+=1,bcc_cut[bcc_cnt]+=cut[V[now]],belong[V[now]]=bcc_cnt;
					if(now==i) break;
				}
			}
		}else{
			Min(low[u],dfn[v]);
			if(dfn[V[i]]<dfn[F[i]]) st[++top]=i;
		}
	}
}
int main(){
//	freopen("bzoj2730.in","r",stdin);
	while(m=gi()){
		init();
		while(m--) Max(n,u=gi()),Max(n,v=gi()),add(u,v),add(v,u);
		for(int re i=1;i<=n;++i) if(!dfn[i]) tarjan_cut(root=i); clear();
		for(int re i=1;i<=n;++i) if(!dfn[i]) tarjan_bcc(i);
		for(int re i=1;i<=bcc_cnt;++i){
			if(bcc_cut[i]==1) ans1+=1,ans2*=bcc_pnt[i]-1;
			if(bcc_cut[i]==0) ans1+=2,ans2*=1ll*bcc_pnt[i]*(bcc_pnt[i]-1)/2;
		}printf("Case %d: %d %lld\n",++Case,ans1,ans2);
	}
}

割边

一条边连接 ( u , v ) (u,v) (u,v),那么它是割边的条件就是 l o w [ v ] > d f n [ u ] low[v]>dfn[u] low[v]>dfn[u],注意是严格大于。
所以当前节点的 l o w low low值不能用 f a fa fa更新。然而有一个问题,就是当重边存在的时候,是可以用 f a fa fa更新的,因为可以从重边中的一条进入 f a fa fa,所以记 f a fa fa是不靠谱的。考虑记录入边的编号,那么只需要保证当前边不是入边的反向边就能解决上述问题了。这个东西可以利用加边的方式,用异或来表示一组双向边。注意这样的话 c n t cnt cnt要初始化为1。

模板
其实这个题也含有割点模板。

#include<bits/stdc++.h>
#define pii std::pair<int,int>
#define mp std::make_pair
#define ll long long
#define pb push_back
#define re register
#define se second
#define cs const
#define fi first
cs int N=2e4+10,M=1e5+10;
namespace IO{
	cs int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	template<typename T>inline T get(){
		char ch=gc();T x=0,f=1;
		while(!isdigit(ch)){if(ch=='-')f=0;ch=gc();}
		while(isdigit(ch)) x=(x+(x<<2)<<1)+(ch^48),ch=gc();
		return f?x:-x;
	}
	inline int gi(){return get<int>();}
	inline ll gl(){return get<ll>();}
}
using IO::gi;
using IO::gl;
inline void Max(int &x,int y){if(x<y)x=y;}
inline void Min(int &x,int y){if(x>y)x=y;}
inline int max(int x,int y){return (x>y)?x:y;}
inline int min(int x,int y){return (x<y)?x:y;}

int n,m,u,v;
int Head[N],Next[M<<1],V[M<<1],cnt=1;
int dfn[N],low[N],bridge[M<<1],cut[N],root,tot=0;
inline void add(int u,int v){Next[++cnt]=Head[u],V[cnt]=v,Head[u]=cnt;}
inline void clear(){memset(dfn,0,sizeof dfn),tot=0;}
inline void tarjan_bridge(int u,int in_edge){
	dfn[u]=low[u]=++tot;
	for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]]){
		if(!dfn[v]){
			tarjan_bridge(v,i),Min(low[u],low[v]);
			if(low[v]>dfn[u]) bridge[i]=bridge[i^1]=1;
		}else if(i!=(in_edge^1)) Min(low[u],dfn[v]);
	}
}
inline void tarjan_cut(int u,int son=0){
	dfn[u]=low[u]=++tot;
	for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]]){
		if(!dfn[v]){
			++son,tarjan_cut(v),Min(low[u],low[v]);
			cut[u]|=(u==root&&son>1)|(u!=root&&low[v]>=dfn[u]);
		}else Min(low[u],dfn[v]);
	}
}
std::vector<int> pnt;
std::vector<pii> edge;
int main(){
//	freopen("Hiho1183.in","r",stdin);
	n=gi(),m=gi();
	while(m--) u=gi(),v=gi(),add(u,v),add(v,u);
	for(int re i=1;i<=n;++i) if(!dfn[i]) tarjan_bridge(i,0);
	clear();
	for(int re i=1;i<=n;++i) if(!dfn[i]) tarjan_cut(root=i,0);
	for(int re i=1;i<=n;++i) if(cut[i]) pnt.pb(i);
	for(int re i=2;i< cnt;i+=2) if(bridge[i])
		edge.pb((V[i]<V[i^1])?mp(V[i],V[i^1]):mp(V[i^1],V[i]));
	std::sort(edge.begin(),edge.end());
	if(pnt.empty()) printf("Null");
	else for(int re i=0;i<pnt.size();++i) printf("%d ",pnt[i]);
	puts("");
	for(int re i=0;i<edge.size();++i)
		printf("%d %d\n",edge[i].fi,edge[i].se);
}

点双

见上方[矿场搭建]

边双

和强连通分量有些类似,只需要记录一下入边,保证当前边不是入边的反向边即可。
模板

#include<bits/stdc++.h>
#define pii std::pair<int,int>
#define mp std::make_pair
#define ll long long
#define re register
#define se second
#define cs const
#define fi first
cs int N=2e4+10,M=1e5+10;
namespace IO{
	cs int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	template <typename T>
	inline T get(){
		char ch=gc();T f=1,x=0;
		while(!isdigit(ch)){if(ch=='-')f=0;ch=gc();}
		while(isdigit(ch)) x=(x+(x<<2)<<1)+(ch^48),ch=gc();
		return x;
	}
	inline int gi(){return get<int>();}
	inline ll gl(){return get<ll>();}
}
using IO::gi;
using IO::gl;
template<typename T>inline void Min(T &x,T y){if(x>y)x=y;}
template<typename T>inline void Max(T &x,T y){if(x<y)x=y;}
template<typename T>inline T max(T x,T y){return x>y?x:y;}
template<typename T>inline T min(T x,T y){return x<y?x:y;}

int n,m,u,v;
int Head[N],Next[M<<1],V[M<<1],cnt=1;
int dfn[N],low[N],st[N],top=0,tot=0;
int ebc_id[N],belong[N],ebc_cnt=0;
inline void add(int u,int v){Next[++cnt]=Head[u],V[cnt]=v,Head[u]=cnt;}
inline void tarjan(int u,int in_edge){
	dfn[u]=low[u]=++tot,st[++top]=u;
	for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]]){
		if(!dfn[v]) tarjan(v,i),Min(low[u],low[v]);
		else if(i!=(in_edge^1)) Min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u]){
		++ebc_cnt;
		while(1){
			int now=st[top--];
			belong[now]=ebc_cnt;
			Min(ebc_id[ebc_cnt],now);
			if(now==u) break;
		}
	}
}
int main(){
//	freopen("Hihocoder1184.in","r",stdin);
	n=gi(),m=gi(),memset(ebc_id,0x3f,sizeof ebc_id);
	while(m--) u=gi(),v=gi(),add(u,v),add(v,u);
	for(int re i=1;i<=n;++i) if(!dfn[i]) tarjan(i,0);
	printf("%d\n",ebc_cnt);
	for(int re i=1;i<=n;++i)
		printf("%d ",ebc_id[belong[i]]);
}

例题:[POJ3352]Road Construction
求:在一个无向连通图中至少加入多少条边使得不存在割边。
做法:求出边双之后缩点,这会形成一棵树,令叶子节点个数为 l e a f _ c n t leaf\_cnt leaf_cnt,答案即为: ( l e a f _ c n t + 1 ) / 2 (leaf\_cnt+1)/2 (leaf_cnt+1)/2
大概可以理解为在叶子节点之间两两配对。
重复描述一条边不是重边而是同一条边的重复说明
爷是真滴佛辣。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<map>
#include<algorithm>
#define pii std::pair<int,int>
#define mp std::make_pair
#define ll long long
#define re register
#define cs const
cs int N=5e4+10,M=5e4+10;
namespace IO{
	cs int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	template<typename T>
	inline T get(){
		char ch=gc();T f=1,x=0;
		while(!isdigit(ch)){if(ch=='-')f=0;ch=gc();}
		while(isdigit(ch)) x=(x+(x<<2)<<1)+(ch^48),ch=gc();
		return f?x:-x;
	}
	inline int gi(){return get<int>();}
	inline ll gl(){return get<ll>();}
}
using IO::gi;
using IO::gl;
inline void Min(int &x,int y){if(x>y)x=y;}
inline void Max(int &x,int y){if(x<y)x=y;}
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
int n,m,u,v;

int Head[N],Next[M<<1],V[M<<1],in[N],cnt=1,leaf_cnt=0;
int dfn[N],low[N],belong[N],st[N],top=0,tot=0,ebc_cnt=0;
inline void add(int u,int v){Next[++cnt]=Head[u],V[cnt]=v,Head[u]=cnt;}
inline void tarjan(int u,int in_edge){
	dfn[u]=low[u]=++tot,st[++top]=u;
	for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]])
		if(!dfn[v]) tarjan(v,i),Min(low[u],low[v]);
		else if(i^in_edge^1) Min(low[u],low[v]);
	if(low[u]==dfn[u]){
		++ebc_cnt;
		while(1){
			int now=st[top--];
			belong[now]=ebc_cnt;
			if(now==u) break;
		}
	}
}
std::map<pii,int> Link;
int main(){
	//freopen("1094.in","r",stdin);
	n=gi(),m=gi();
	while(m--){
		u=gi(),v=gi();
		if(!Link[mp(u,v)]) add(u,v),add(v,u),Link[mp(u,v)]=Link[mp(v,u)]=1;
	}
	tarjan(1,0);
	for(int re u=1;u<=n;++u)
		for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]])
			in[belong[v]]+=(belong[u]!=belong[v]);
	if(ebc_cnt==1) return puts("0"),0;
	for(int re i=1;i<=ebc_cnt;++i) leaf_cnt+=(in[i]==1);
	printf("%d\n",(leaf_cnt+1)>>1);
}
深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值