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

早过忘川 提交于 2019-11-30 18:19:48

树与图的遍历

时间复杂度 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\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表示边数

  • 不需要初始化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表示点数

/*
    *如果有负权边,虽然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;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!