模型总结-图论

最短路基础算法

**加粗样式**

dijkstra 邻接矩阵版

初始化
for n-1int t=-1;
	for 找出未标记最小点
	for 松弛边
	st标记
	剪枝
void djs()
{
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    
    for(int i=0;i<n-1;i++)
    {
        int t=-1;
        for(int j=1;j<=n;j++)
            if(!st[j]&&(t==-1||dist[t]>dist[j])) t=j;
        
        for(int j=1;j<=n;j++)
            dist[j]=min(dist[j],dist[t]+g[t][j]);
        
        st[t]=1;
        if(st[n]) return;
    }
}

dijkstra 邻接表版

初始化
while 不空
	弹出堆顶(top pop)
	判断标记st	
	for 松弛边并入堆
	剪枝
void djs()
{
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    priority_queue<PII,vector<PII>,greater<PII>> heap;
    heap.push({0,1});
    
    while(heap.size())
    {
        int t=heap.top().y;
        heap.pop();
        
        if(st[t]) continue;
        st[t]=1;
        
        for(int i=h[t];~i;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>dist[t]+w[i])
            {
                dist[j]=dist[t]+w[i];
                heap.push({dist[j],j});
            }
        }
        if(st[n]) return;
    }
}

spfa

初始化 循环队列
while 不空
	弹出元素
	标记不在队列中
	for 边
	更新dist 并将没在队列中点加入队列
void spfa()
{
    memset(dist,0x3f,sizeof dist);
    int hh=0,tt=0;
    dist[1]=0;
    st[1]=0;
    q[tt++]=1;
    
    while(hh!=tt)
    {
        int t=q[hh++];
        if(hh==N) hh=0;
        st[t]=false;
        
        for(int i=h[t];~i;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>dist[t]+w[i])
            {
                dist[j]=dist[t]+w[i];
                if(!st[j])
                {
                    q[tt++]=j;
                    if(tt==N) tt=0;
                    st[j]=true;
                }
            }
        }
    }
}

bellman_ford

一般不用
特殊用法 可以求出一个点到其它点最多经过k条边的最短路
void bellman_ford()
{
    memset(dist, 0x3f, sizeof dist);

    dist[1] = 0;
    for (int i = 0; i < k; i ++ )
    {
        memcpy(last, dist, sizeof dist);
        for (int j = 0; j < m; j ++ )
        {
            auto e = edges[j];
            dist[e.b] = min(dist[e.b], last[e.a] + e.c);
        }
    }
}

floyd

经过前k个点i到j的最短距离
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(e[i][j]>e[i][k]+e[k][j]) e[i][j]=e[i][k]+e[k][j];

单源最短路

拓展变形

最短路+dfs 例如当要求必须经过某几个点又不要求经过点的顺序 
则可以用dfs枚举出经过这几个点顺序 在进行求解

最短路+二分 可以控制条件改变边的状态进行二分的check()

最短路+拓扑排序 可以先将图缩点为拓扑图 在在缩点内做最短路。
按拓扑序扫描点时当 扫描到一个点时该点不会在被更新

最短路+dp 当一个图中依赖关系有环时 则可以考虑使用最短路算法求解。
需要注意的是 能否用 djs 要看是否每个点出队时是否还会被更新。spfa一般没限制

拆点 当一个点的状态不仅有坐标信息时可以增加状态信息 类比 dp 进行状态转移建边

最短路方案数 可以利用dp的方式记录  需要注意的是 djs 点出堆顺序满足拓扑序 但spfa不满足

次短路

typedef long long LL;

const int N = 100010, M = 300010, INF = 0x3f3f3f3f;

int n, m;
struct Edge
{
    int a, b, w;
    bool used;
    bool operator< (const Edge &t) const
    {
        return w < t.w;
    }
}edge[M];
int p[N];
int h[N], e[M], w[M], ne[M], idx;
int depth[N], fa[N][17], d1[N][17], d2[N][17];
int q[N];

void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

LL kruskal()
{
    for (int i = 1; i <= n; i ++ ) p[i] = i;
    sort(edge, edge + m);
    LL res = 0;
    for (int i = 0; i < m; i ++ )
    {
        int a = find(edge[i].a), b = find(edge[i].b), w = edge[i].w;
        if (a != b)
        {
            p[a] = b;
            res += w;
            edge[i].used = true;
        }
    }

    return res;
}

void build()
{
    memset(h, -1, sizeof h);
    for (int i = 0; i < m; i ++ )
        if (edge[i].used)
        {
            int a = edge[i].a, b = edge[i].b, w = edge[i].w;
            add(a, b, w), add(b, a, w);
        }
}

void bfs()
{
    memset(depth, 0x3f, sizeof depth);
    depth[0] = 0, depth[1] = 1;
    q[0] = 1;
    int hh = 0, tt = 0;
    while (hh <= tt)
    {
        int t = q[hh ++ ];
        for (int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i];
            if (depth[j] > depth[t] + 1)
            {
                depth[j] = depth[t] + 1;
                q[ ++ tt] = j;
                fa[j][0] = t;
                d1[j][0] = w[i], d2[j][0] = -INF;
                for (int k = 1; k <= 16; k ++ )
                {
                    int anc = fa[j][k - 1];
                    fa[j][k] = fa[anc][k - 1];
                    int distance[4] = {d1[j][k - 1], d2[j][k - 1], d1[anc][k - 1], d2[anc][k - 1]};
                    d1[j][k] = d2[j][k] = -INF;
                    for (int u = 0; u < 4; u ++ )
                    {
                        int d = distance[u];
                        if (d > d1[j][k]) d2[j][k] = d1[j][k], d1[j][k] = d;
                        else if (d != d1[j][k] && d > d2[j][k]) d2[j][k] = d;
                    }
                }
            }
        }
    }
}

int lca(int a, int b, int w)
{
    static int distance[N * 2];
    int cnt = 0;
    if (depth[a] < depth[b]) swap(a, b);
    for (int k = 16; k >= 0; k -- )
        if (depth[fa[a][k]] >= depth[b])
        {
            distance[cnt ++ ] = d1[a][k];
            distance[cnt ++ ] = d2[a][k];
            a = fa[a][k];
        }
    if (a != b)
    {
        for (int k = 16; k >= 0; k -- )
            if (fa[a][k] != fa[b][k])
            {
                distance[cnt ++ ] = d1[a][k];
                distance[cnt ++ ] = d2[a][k];
                distance[cnt ++ ] = d1[b][k];
                distance[cnt ++ ] = d2[b][k];
                a = fa[a][k], b = fa[b][k];
            }
        distance[cnt ++ ] = d1[a][0];
        distance[cnt ++ ] = d1[b][0];
    }

    int dist1 = -INF, dist2 = -INF;
    for (int i = 0; i < cnt; i ++ )
    {
        int d = distance[i];
        if (d > dist1) dist2 = dist1, dist1 = d;
        else if (d != dist1 && d > dist2) dist2 = d;
    }

    if (w > dist1) return w - dist1;
    if (w > dist2) return w - dist2;
    return INF;
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; i ++ )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        edge[i] = {a, b, c};
    }

    LL sum = kruskal();
    build();
    bfs();

    LL res = 1e18;
    for (int i = 0; i < m; i ++ )
        if (!edge[i].used)
        {
            int a = edge[i].a, b = edge[i].b, w = edge[i].w;
            res = min(res, sum + lca(a, b, w));
        }
    printf("%lld\n", res);

    return 0;
}

Floyd算法扩展

传递闭包

求传递闭包就是可以 求出所有点的传递关系
例如 a>b  b>c  则a>c
可以利用 floyd算法求解 传递闭包  但g[i][i]=1时一般代表矛盾
 memset(g, 0, sizeof g);
 g[i][j]=1 //将有关系的点名为一
 for(int i=1;k<=n;k++)
     for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++)
         {
             if(g[i][k]&g[k][j]) g[i][j]=1;
         }

找最小环

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;

const int N = 109,INF=0x3f3f3f3f;
int n,m;
int g[N][N],d[N][N];
int path[N];
int cnt;
int pos[N][N];

void getp(int x,int y)
{
    if(pos[x][y]==0) return ;
    
    int k=pos[x][y];
    getp(x,k);
    path[cnt++]=k;
    getp(k,y);
}

int main()
{
    cin>>n>>m;
    
    memset(g,0x3f,sizeof g);
    int a,b,c;
    for(int i=0;i<m;i++)
    {
        cin>>a>>b>>c;
        g[a][b]=min(g[a][b],c);
        g[b][a]=c;
    }
    
    memcpy(d,g,sizeof g);
    int ans=INF;
    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<k;i++)
        {
            for(int j=i+1;j<k;j++)
            {
                if((LL)d[i][j]+g[i][k]+g[k][j]<ans)
                {
                    ans=d[i][j]+g[i][k]+g[k][j];
                    cnt=0;
                    path[cnt++]=i;
                    getp(i,j);
                    path[cnt++]=j;
                    path[cnt++]=k;
                }
            }
        }
        
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(d[i][k]+d[k][j]<d[i][j])
                {
                    d[i][j]=d[i][k]+d[k][j];
                    pos[i][j] = k;
                }
            }
        }
    }
    
    if(ans==INF) cout<<"No solution.";
    else
    {
        for(int i=0;i<cnt;i++) cout<<path[i]<<' ';
    }
    
    return 0;
}

恰好经过k条边最短路

利用倍增的思想和flody结合进行求解
#include<bits/stdc++.h>
using namespace std;

const int N=1009,M=109;
int idx;
unordered_map<int,int> mmap;
int n,T,s,e;
int g[N][N],t[N][N];

int getx(int x)
{
    if(mmap.count(x)) return mmap[x];
    else
    {
        mmap[x]=++idx;
        return  idx;
    }
}

void mul(int c[][N],int a[][N],int b[][N])
{
    memset(t,0x3f,sizeof t);
    
    for(int k=1;k<=idx;k++)
        for(int i=1;i<=idx;i++)
            for(int j=1;j<=idx;j++)
                if(a[i][k]+b[k][j]<t[i][j]) t[i][j]=a[i][k]+b[k][j];
    memcpy(c,t,sizeof t);
}

int quick(int k)
{
    int ret[N][N];
    memset(ret,0x3f,sizeof ret);
    for(int i=0;i<=idx;i++) ret[i][i]=0;
    
    while (k)
    {
        if (k & 1) mul(ret,ret,g);
        mul(g,g,g);
        k >>= 1;
    }
    
    return ret[getx(s)][getx(e)];
}


int main()
{
    cin>>n>>T>>s>>e;
    
    memset(g,0x3f,sizeof g);
    int a,b,c;
    for(int i=0;i<T;i++)
    {
        cin>>c>>a>>b;
        a=getx(a);
        b=getx(b);
        g[a][b]=min(g[a][b],c);
        g[b][a]=g[a][b];
    }

    cout<<quick(n)<<endl;;
    
    return 0;
}

最小生成树

在解题时 如果越到要求有必选边 则提前合并即可。并且在克鲁斯卡尔算法中
会优先使用短边,即用最少边和连接最多点。
求一棵树的完全图且改图的最小生成树为原图 可以根据克鲁斯卡尔算法的
合并顺序进行求解即可
int prim()
{
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    
    int ret=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(dist[t]==INF) return INF;
        
        st[t]=true;
        ret+=dist[t];
        for(int j=1;j<=n;j++)
        {
            dist[j]=min(dist[j],g[t][j]);
        }
    }
    return ret;
}

//并查集
int find(int x)
{
    if(p[x]!=x) p[x]=find(p[x]);
    return p[x];
}
int kruskal()
{
    sort(edges,edges+m);
    
    for(int i=1;i<=n;i++) p[i]=i;
    
    int ret=0,cnt=0;
    for(int i=0;i<m;i++)
    {
        int a=edges[i].a,b=edges[i].b,w=edges[i].w;
        
        a=find(a),b=find(b);
        if(a!=b)
        {
            p[a]=b;
            ret+=w;
            cnt++;
        }
    }
    
    if(cnt<n-1) return INF;
    return ret;
    
}

spfa求负环

在求解负环的题中 可以尝试将spfa中的循环队列该为站,一般情况下可以加速求解速度
但是在求解最短路的题中切勿使用
int h[N], e[M], w[M], ne[M], idx;
int q[N], dist[N],cnt[N];
bool st[N];
int n,m,W;

void add(int a, int b, int c)  // 添加一条边a->b,边权为c
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

bool spfa()  // 求1号点到n号点的最短路距离
{
    int hh = 0, tt = -1;
    memset(dist, 0, sizeof dist);
    memset(cnt,0,sizeof cnt);
    memset(st, 0, sizeof st);
    
    for(int i=1;i<=n;i++)
    {
        q[++tt] = i;
        st[i] = true;
    }

    while (hh<=tt)
    {
        int t = q[tt--];
        // if (hh == N) hh = 0;
        st[t] = false;
        
        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                cnt[j]=cnt[t]+1;
                if(cnt[j]>=n) return true;
                if (!st[j])
                {
                    q[++tt] = j;
                    //if (tt == N) tt = 0;
                    st[j] = true;
                }
            }
        }
    }
    return false;
}

差分约束

应用:解不等式组
在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;

const int N = 1e5+9,M=3e5+9;
int h[N],e[M],w[M],ne[M],idx;
int n,k;
int q[N];
int dist[N];
bool st[N];
int cnt[N];

void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

LL spfa()
{
    memset(dist,-0x3f,sizeof dist);
    dist[0]=0;
    int hh=0,tt=0;
    q[tt++]=0;
    st[0]=true;
    
    while(hh!=tt)
    {
        int t=q[--tt];
        // if(hh==N) hh=0;
        st[t]=false;
        
        for(int i=h[t];~i;i=ne[i])
        {
            int j=e[i];
            if(dist[j]<dist[t]+w[i])
            {
                dist[j]=dist[t]+w[i];
                cnt[j]=cnt[t]+1;
                if(cnt[j]>n) return -1;
                
                if(!st[j])
                {
                    q[tt++]=j;
                    // if(tt==N) tt=0;
                    st[j]=true;
                }
            }
        }
    }
    
    LL ret=0;
    for(int i=1;i<=n;i++)
    {
        ret+=dist[i];
    }
    return ret;
}

int main()
{
    // cin>>n>>k;
    scanf("%d%d",&n,&k);
    
    int a,b,c;
    memset(h,-1,sizeof h);
    //构建不同边
    for(int i=0;i<k;i++)
    {
        // cin>>c>>a>>b;
        scanf("%d%d%d",&c,&a,&b);
        if(c==1)
        {
            add(a,b,0),add(b,a,0);
        }else if(c==2)
        {
            add(a,b,1);
        }else if(c==3)
        {
            add(b,a,0);
        }else if(c==4)
        {
            add(b,a,1);
        }else if(c==5)
        {
            add(a,b,0);
        }
    }
    //构建绝对值边
    for(int i=1;i<=n;i++)
    {
        add(0,i,1);
    }
    
    cout<<spfa()<<endl;
    
    return 0;
}

最近公共祖先(LCA)

**加粗样式**

void bfs(int root)  // 预处理倍增数组
{
    memset(depth, 0x3f, sizeof depth);
    depth[0] = 0, depth[root] = 1;  // depth存储节点所在层数 0为无效哨兵
    int hh = 0, tt = 0;
    q[tt++] = root;
    while (hh != tt)
    {
        int t = q[hh ++ ];
        for (int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i];
            if (depth[j] > depth[t] + 1)
            {
                depth[j] = depth[t] + 1;
                q[tt++] = j;
                fa[j][0] = t;  // j的第二次幂个父节点
                for (int k = 1; k <= 15; k ++ )
                    fa[j][k] = fa[fa[j][k - 1]][k - 1];
            }
        }
    }
}

int lca(int a, int b)  // 返回a和b的最近公共祖先
{
    if (depth[a] < depth[b]) swap(a, b);
    for (int k = 15; k >= 0; k -- )
        if (depth[fa[a][k]] >= depth[b])
            a = fa[a][k];
    if (a == b) return a;
    for (int k = 15; k >= 0; k -- )
        if (fa[a][k] != fa[b][k])
        {
            a = fa[a][k];
            b = fa[b][k];
        }
    return fa[a][0];
}

 	int root = 0;
    memset(h, -1, sizeof h);

    for (int i = 0; i < n; i ++ )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        if (b == -1) root = a;
        else add(a, b), add(b, a);
    }
    bfs(root);
//离线 线性做法
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

typedef pair<int, int> PII;

const int N = 10010, M = N * 2;

int n, m;
int h[N], e[M], w[M], ne[M], idx;
int dist[N];
int p[N];
int res[M];
int st[N];
vector<PII> query[N];   // first存查询的另外一个点,second存查询编号

void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

void dfs(int u, int fa)
{
    for (int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];
        if (j == fa) continue;
        dist[j] = dist[u] + w[i];
        dfs(j, u);
    }
}

int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

void tarjan(int u)
{
    st[u] = 1;
    for (int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];
        if (!st[j])
        {
            tarjan(j);
            p[j] = u;
        }
    }

    for (auto item : query[u])
    {
        int y = item.first, id = item.second;
        if (st[y] == 2)
        {
            int anc = find(y);
            res[id] = dist[u] + dist[y] - dist[anc] * 2;
        }
    }

    st[u] = 2;
}

int main()
{
    scanf("%d%d", &n, &m);
    memset(h, -1, sizeof h);
    for (int i = 0; i < n - 1; i ++ )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c), add(b, a, c);
    }

    for (int i = 0; i < m; i ++ )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        if (a != b)
        {
            query[a].push_back({b, i});
            query[b].push_back({a, i});
        }
    }

    for (int i = 1; i <= n; i ++ ) p[i] = i;

    dfs(1, -1);
    tarjan(1);

    for (int i = 0; i < m; i ++ ) printf("%d\n", res[i]);

    return 0;
}

有向图的强连通分量(SCC)

在这里插入图片描述
还可以 求最大半连通子图 先缩点在dp求最长路即可
求最长路/差分约束(边权非负)先缩点在建图,建图中看缩点内是否边权均为0如果不满足代表无界 ,最后按拓扑序dp最长路即可


1. 加时间戳;
2. 放入栈中,做好标记;
3. 遍历邻点
    1)如果没遍历过,tarjan一遍,用low[j]更新最小值low
    2) 如果在栈中,用dfn[j]更新最小值low
4.找到最高点
    1)scc个数++
    2do-while循环:
        从栈中取出每个元素;标志为出栈;
        对元素做好属于哪个scc;该scc中点的数量++
int dfn[N], low[N], timestamp;
int stk[N], top;
bool in_stk[N];
int id[N], scc_cnt, scc_size[N];

void tarjan(int u)
{
    dfn[u] = low[u] = ++ timestamp;
    stk[ ++ top] = u, in_stk[u] = true;
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!dfn[j])
        {
            tarjan(j);
            low[u] = min(low[u], low[j]);
        }
        else if (in_stk[j]) low[u] = min(low[u], dfn[j]);
    }

    if (dfn[u] == low[u])
    {
        ++ scc_cnt;
        int y;
        do {
            y = stk[top -- ];
            in_stk[y] = false;
            id[y] = scc_cnt;
            scc_size[scc_cnt] ++ ;
        } while (y != u);
    }
}

建图缩点
forif(!dfn[]) tarjan();
拓扑序为逆序
for(int i=scc_cnt;i>0;i--)

无向图的边连通分量(e-DCC)

无向图的点连通分量(v-DCC)

二分图

二分图,即可以将一个 无向图 划分为两个集合,是的集合的点没有边相连
在这里插入图片描述
应用:
1、棋盘二分图,给棋盘最多放多少1*2骨牌 日子行

//二分图判定
int color[N];
bool dfs(int u,int c)
{
    color[u]=c;
    
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(!color[j])
        {
            if(!dfs(j,3-c)) return 0;
        }else if(color[j]==c) return 0;
    }
    
    return 1;
}

    bool flag=1;
    for(int i=1;i<=n;i++)
    {
        if(!color[i])
        {
            if(!dfs(i,1))
            {
                flag=false;
                break;
            }
        }
    }
    
    if(flag) cout<<"Yes";
    else cout<<"No";
//二分图的最大匹配
int match[N];
bool st[N];

bool find(int x)
{
    for (int i = h[x]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j])
        {
            st[j] = true;
            if (match[j] == 0 || find(match[j]))
            {
                match[j] = x;
                return true;
            }
        }
    }
    
    return false;
}
    int ret=0;
    for(int i=1;i<=n1;i++)
    {
        memset(st,false,sizeof st);
        if(find(i)) ret++;
    }

欧拉路径/回路

应用:一笔画问题
在这里插入图片描述
普通版本
时间复杂度:最坏情况下 o ( m 2 ) o(m^2) o(m2)

void dfs(int u)
{
    for(int i=h[u];~i;i=ne[i])
    {
        if(st[i]) continue;
        
        st[i]=true;
        if(t==1) st[i^1]=true;//无向图时需将方向标也标记
        
        dfs(e[i]);
        ans[cnt++]=?? //记录答案 注意第一个点的初始化      
    }
}

//输出路径
for(int i=cnt-1;i>=0;i--) cout<<ans[i]<<' ';

环路优化
时间复杂度: O ( m ) O(m) O(m)

void dfs(int u)
{
    for(int i=h[u];~i;i=h[u])
    {
        if(st[i])
        {
            h[u]=ne[i];
            continue;
        }
        h[u]=ne[i];
        
        st[i]=true;
        if(t==1) st[i^1]=true;//无向图时需将方向标也标记
        
        dfs(e[i]);
        ans[cnt++]=?? //记录答案        
    }
}

//输出路径
for(int i=cnt-1;i>=0;i--) cout<<ans[i]<<' ';

拓扑排序

满足拓扑排序的充要条件是改图为有向无环图(DAG)。
应用:
1、可以与dp结合,用拓扑序进行dp求解
2、最长路(有限制,具体看最长路几种求解)

时间复杂度: O ( n + m ) O(n+m) O(n+m)

int n;
int q[N],d[N];
int h[N], e[M], ne[M], idx;

void topsort()
{
    int hh = 0, tt = -1;

    // d[i] 存储点i的入度
    for (int i = 1; i <= n; i ++ )
        if (!d[i])
            q[ ++ tt] = i;

    while (hh <= tt)
    {
        int t = q[hh ++ ];

        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (-- d[j] == 0)
                q[ ++ tt] = j;
        }
    }
}
//输出拓扑序
for(int i=0;i<n;i++) cout<<q[i]<<' ';

其它tips

最长路

最短路算法不仅可以求最短路 也可以求最长路
只需要更改 判断条件即可  dist[j]=max(dist[j],dist[[t]+w);

在这里插入图片描述

虚拟源点

可以在图中建立一些虚拟的点解决一些 多起点问题。

路径权重

最短路算法不仅可以处理路径的加减 还可以应用到乘除
当权值 >=1 时 可以用 djs 当权值 >0 只能用 spfa
初始化一般为 1  不为 0

建图

建图时可以考虑 边点交换
单词建图时可以考虑将每个字母作为一个点(作为边)
虚拟源点
起点和终点是否可以交换
注意重边是否影响结果
邻接矩阵是否需要初始化自己到自己为0
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值