zoukankan      html  css  js  c++  java
  • 【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表示边数

    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表示边数

    
    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
    ", 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表示边数

    • 不需要初始化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
    ");
        else 
            printf("No
    ");
        return 0;
    }
    

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

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

    /*
    	*如果有负权边,虽然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
    ");///////////////////////
        	else printf("%d
    ",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
    ");
    	else printf("%lld
    ",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
    ");
    	else printf("No
    ");
    	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;
    }
    
  • 相关阅读:
    无向图求所有路径C#版
    centos 7 无痛安装 es
    rust 实现协程池
    spring boot fat jar 引入新的jar 文件到classpath
    nicolaka/netshoot 强大的容器网络问题解决工具
    使用nsenter 解决tcpcollect容器网络捕捉问题
    centraldogma git 镜像配置
    jmespath java 使用
    使用apicurio-registry 管理schema
    bfe 简单学习示例
  • 原文地址:https://www.cnblogs.com/sjsjsj-minus-Si/p/11634705.html
Copyright © 2011-2022 走看看