【acwing板子大全】图论与搜索

树与图的遍历

时间复杂度 O(n+m), n表示点数,m表示边数算法

(1) 深度优先遍历 —— 模板题 AcWing 846. 树的重心数组

int dfs(int u){
	vis[u]=1;
	int son_size=1,now=0;
	ee(i,u){
		int v=e[i].v;
		if(!vis[v]){
			int t=dfs(v);
			now=max(now,t);
			son_size+=t;
		}
	}
	now=max(now,n-son_size);
	ans=min(ans,now);
	return son_size;
}

(2) 宽度优先遍历 —— 模板题 AcWing 847. 图中点的层次优化

queue<int>q;
inline void spfa(){
	mem(dis,0x3f);
	mem(vis,0);
	q.push(1);
	vis[1]=1;
	dis[1]=0;
	while(!q.empty()){
		int u=q.front();q.pop();
		vis[u]=0;
		ee(i,u){
			int v=e[i].v,w=e[i].w;
			if(dis[v]>dis[u]+w){
				dis[v]=dis[u]+w;
				if(!vis[v]){
					vis[v]=1;
					q.push(v);
				}
			}
		}
	}
}

拓扑排序 —— 模板题 AcWing 848. 有向图的拓扑序列 时间复杂度 O(n+m), n表示点数,m表示边数spa

queue<int>q;
bool topo(){
	rep(i,1,n)
		if(!deg[i])
			q.push(i);
	while(!q.empty()){
		int u=q.front();q.pop();
		topon[++tot]=u;
		ee(i,u){
			int v=e[i].v;
			if(--deg[v]==0)
				q.push(v);
		}
	}
	return tot==n;
}

堆优化版dijkstra —— 模板题 AcWing 850. Dijkstra求最短路 II

时间复杂度 O(mlogn), n表示点数,m表示边数code

priority_queue<pair<int,int> >q; 

inline void dij(){
	mem(dis,0x3f);
	mem(vis,0);
	q.push(make_pair(0,1));
	dis[1]=0;
	while(!q.empty()){
		int u=q.top().second;q.pop();
		if(vis[u])continue;		
		vis[u]=1;
		ee(i,u){
			int v=e[i].v,w=e[i].w;
			if(dis[v]>dis[u]+w){
				dis[v]=dis[u]+w;
				q.push(make_pair(-dis[v],v));
			}
		}
	}
}

Bellman-Ford算法 —— 模板题 AcWing 853. 有边数限制的最短路

时间复杂度 O(nm), n表示点数,m表示边数排序

#define N 510
#define M 10010

int n,m,k;
int dis[N],backup[N];
//使用backup数组的目的是为了防止松弛的次数大于k

struct Edge{
    int u, v, w;
}e[M];

inline int bellman_ford(){
	mem(dis,0x3f);
	dis[1]=0;
	rep(i,1,k){
		memcpy(backup,dis,sizeof(dis));
		rep(j,1,m){
			int u=e[j].u,v=e[j].v,w=e[j].w;
			if(backup[u]!=0x3f3f3f3f && dis[v]>backup[u]+w){
				dis[v]=backup[u]+w;
			}
		}
	}
	if(dis[n]==0x3f3f3f3f)return -1;
	else return dis[n];
}

int main(){
	rd(n),rd(m),rd(k);
    rep(i,1,m){
    	rd(e[i].u),rd(e[i].v),rd(e[i].w);
    }
    int t=bellman_ford();
    if (t==-1) puts("impossible");
    else printf("%d\n", t);
    return 0;
}

spfa 算法(队列优化的Bellman-Ford算法) —— 模板题 AcWing 851. spfa求最短路

时间复杂度 平均状况下 O(m),最坏状况下 O(nm), n表示点数,m表示边数队列

queue<int>q;
inline void spfa(){
	mem(dis,0x3f);
	mem(vis,0);
	q.push(1);
	vis[1]=1;
	dis[1]=0;
	while(!q.empty()){
		int u=q.front();
		q.pop();
		vis[u]=0;
		ee(i,u){
			int v=e[i].v,w=e[i].w;
			if(dis[v]>dis[u]+w){
				dis[v]=dis[u]+w;
				if(!vis[v]){
					vis[v]=1;
					q.push(v);
				}
			}
		}
	}
}

spfa判断图中是否存在负环 —— 模板题 AcWing 852. spfa判断负环

时间复杂度是 O(nm), n表示点数,m表示边数it

  • 不须要初始化dist数组 原理:若是某条最短路径上有n个点(除了本身),那么加上本身以后一共有n+1个点,由抽屉原理必定有两个点相同,因此存在环。
  • cnt数组表示到达当前这个点最短路的边数,若是cnt[x]>=n,说明至少通过了n条边,即n+1个点,由抽屉原理可知显然有两个点重复,即存在负环
#define N 100010

int head[N],tot,dis[N],cnt[N];
bool vis[N];

struct Edge{
	int v,next,w;
}e[N<<1];

void add(int u,int v,int w){e[++tot].v=v;e[tot].w=w;e[tot].next=head[u];head[u]=tot;}

int n,m,s;

queue<int>q;
inline bool spfa(int s){
	mem(dis,0)//本题能够不作初始化,但加上了也没事,因此就加上呗
	mem(vis,0);
	mem(cnt,0);
    rep(i,1,n){
        vis[i]=1;
        q.push(i);
    }////判整个图的负环要将每一个节点都加入
	while(!q.empty()){
		int u=q.front();
		q.pop();
		vis[u]=0;
		ee(i,u){
			int v=e[i].v,w=e[i].w;
			if(dis[v]>dis[u]+w){
				dis[v]=dis[u]+w;
				cnt[v]=cnt[u]+1;
				if(cnt[v]>=n)return 1;
				if(!vis[v]){
					vis[v]=1;
					q.push(v);
				}
			}
		}
	}
	return 0;
}

int main(){
    rd(n),rd(m);
    rep(i,1,m){
		int x,y,z;rd(x),rd(y),rd(z);
        add(x,y,z);
    }
    if(spfa(1))
    	printf("Yes\n");
    else 
        printf("No\n");
    return 0;
}

floyd算法 —— 模板题 AcWing 854. Floyd求最短路

时间复杂度是 O(n^3) , n表示点数io

/*
	*若是有负权边,虽然a,b不连通,但之间的距离也会由于负权边的更新小于INF,
	由于INF/2也是一个很大的数,因此就用>INF/2来表明不能连通

#define N 210
int dis[N][N];
int n,m,k;

inline void floyd(){
	rep(k,1,n)
		rep(i,1,n)
			rep(j,1,n)
				dis[i][j]=min(dis[i][k]+dis[k][j],dis[i][j]);
}


int main(){
    rd(n),rd(m),rd(k);
    mem(dis,0x3f);
    rep(i,1,n)dis[i][i]=0;/////////////////////////////
    rep(i,1,m){
		int x,y,z;rd(x),rd(y),rd(z);
        dis[x][y]=min(dis[x][y],z);///////////////////////
    }
    floyd();
    while(k--){
    	int x,y;rd(x),rd(y);
    	if(dis[x][y]>0x3f3f3f3f/2)printf("impossible\n");///////////////////////
    	else printf("%d\n",dis[x][y]);
	}
	//printf("%d",0x3f3f3f3f);
    return 0;
}

朴素版prim算法 —— 模板题 AcWing 858. Prim算法求最小生成树

时间复杂度是 O(n^2+m), n表示点数,m表示边数模板

int n; // n表示点数 int g[N][N]; // 邻接矩阵,存储全部边 int dist[N]; // 存储其余点到当前最小生成树的距离 bool st[N]; // 存储每一个点是否已经在生成树中

// 若是图不连通,则返回INF(值是0x3f3f3f3f), 不然返回最小生成树的树边权重之和 int prim() { memset(dist, 0x3f, sizeof dist);

int res = 0;
for (int i = 0; i < n; i ++ )
{
    int t = -1;
    for (int j = 1; j <= n; j ++ )
        if (!st[j] && (t == -1 || dist[t] > dist[j]))
            t = j;

    if (i && dist[t] == INF) return INF;

    if (i) res += dist[t];
    st[t] = true;

    for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);
}

return res;

}

Kruskal算法 —— 模板题 AcWing 859. Kruskal算法求最小生成树

时间复杂度是 O(mlogm), n表示点数,m表示边数

#define N 100010

struct edge{
	int u,v,w;
	bool operator < (const edge &rhs)const{
		return w<rhs.w;
	}
}e[N<<1];

int f[N],n,m,ans,cnt;

inline int find(int x){return x==f[x]?x:f[x]=find(f[x]);}

inline void kruskal(){
	sort(e+1,e+m+1);
	rep(i,1,n)f[i]=i;
	rep(i,1,m){
		int fx=find(e[i].u),fy=find(e[i].v);
		if(fx==fy)continue;
		f[fx]=fy;
		ans+=e[i].w;
		if(++cnt==n-1)break;
	}
}


#undef int
int main(){
#define int long long
	#ifdef WIN32
	freopen("","r",stdin);
	#endif
	rd(n),rd(m);
	rep(i,1,m){
		rd(e[i].u),rd(e[i].v),rd(e[i].w);
	}
	kruskal();
	if(cnt<n-1)printf("impossible\n");
	else printf("%lld\n",ans);
	return 0;
}

染色法判别二分图 —— 模板题 AcWing 860. 染色法断定二分图

时间复杂度是 O(n+m), n表示点数,m表示边数

#define N 200010
#define M 

int head[N],tot,col[N],s1,cnt,n,m;
bool vis[N];
//col 表示每一个点的颜色,-1表示未染色,-2表示白色,1表示黑色

struct edge{
	int v,w,next;
}e[N<<1];

inline void add(int u,int v){e[++tot].v=v;e[tot].next=head[u];head[u]=tot;}

queue<pair<int,int> >q;
inline bool bfs(){
	col[1]=1;
	q.push(make_pair(1,1));
	while(!q.empty()){
		int u=q.front().first;
		int color=q.front().second;
		q.pop();
		ee(i,u){
			int v=e[i].v;
			if(col[v]==-1){
				col[v]=~color;
				q.push(make_pair(v,col[v]));
			}
			else if(col[v]==col[u])return 0; 
		}
	}
	return 1;
} 

int main(){
//freopen("erfen.txt","r",stdin);
	rd(n),rd(m);
	rep(i,1,m){
		int u,v;rd(u),rd(v);
		add(u,v),add(v,u);
	}
	mem(col,-1);
	if(bfs())printf("Yes\n");
	else printf("No\n");
	return 0;
} 
/*
4 4
1 3
1 4
2 3
2 4
*/
//Yes
//col的值有三种,-1(初始值),-2,1;

匈牙利算法 —— 模板题 AcWing 861. 二分图的最大匹配

时间复杂度是 O(nm), n表示点数,m表示边数

#define N 510
#define M 100010
int head[N],cnt; 

struct edge{
	int v,next;  
}e[M];

inline void add(int u,int v){e[++cnt].v=v;e[cnt].next=head[u];head[u]=cnt;}
int match[N];
bool vis[N];//考虑过 
int n1,n2,m,ans;

inline bool dfs(int u){
	ee(i,u){
		int v=e[i].v;
		if(!vis[v]){
			vis[v]=1;
			if(match[v]==0 || dfs(match[v])){
				match[v]=u;
				return 1;
			}
		}
	}
	return 0;
}

#undef int
int main(){
#define int long long
	//freopen("erfen.txt","r",stdin);
	rd(n1),rd(n2),rd(m);
	while(m--){
		int u,v;rd(u),rd(v);
		add(u,v);
	}
	mem(match,0);
	rep(i,1,n1){
		mem(vis,0);
		if(dfs(i))
			ans++;
	}
	printf("%lld",ans);
	return 0;
}