图论板子总结

一、单源最短路问题1 Dijstra:无负权图
1.邻接矩阵:O(|V|*|V|)

int G[maxn][maxn];
int d[maxn];
bool vis[maxn];
int n;

void Dijkstra(int s)
{
    for(int i=1;i<=n;i++){
       d[i] = inf;
       vis[i] = false; 
    }
    d[s] = 0;
    
    while(1){
        int u = -1;
        for(int v=1;v<=n;v++){
            if(!vis[v]&&(u==-1||d[u]>d[v])){
                u = v;
            }
        }
        if(u==-1) break;
        vis[u] = true;
        for(int v=1;v<=n;v++){
            d[v] = min(d[v],d[u]+G[u][v]);
        }
    }
}

2.邻接表:O(|E|log|V|)

struct edge{
    int to,cost;
};
typedef pair<int,int> P;    //fisrt是最短距离,second是顶点编号
int n;
vector<edge> Adj[maxn];
void Init()
{
    for(int i=1;i<=n;i++)
        Adj[i].clear();
}
void add(int u,int v,int c)
{
    edge tmp;
    tmp.to = v;
    tmp.cost = c;
    Adj[u].push_back(tmp);
}
void Dijsktra(int s)
{
    //通过制定greater<P>参数,堆按照first从小到大的顺序取出值
    priority_queue<P,vector<P>,greater<P>> q;
    for(int i=1;i<=n;i++)
        d[i] = inf;
    d[s] = 0;
    q.push(P(0,s));
    
    while(!q.empty()){
        P p = q.top();
        q.pop();
        int v = p.second;
        if(d[v] < p.first)  continue;
        for(int i=0;i<Adj[v].size();i++){
            edge e = Adj[v][i];
            if(d[e.to]>d[v] + e.cost){
                d[e.to] = d[v] + e.cost;
                q.push(P(d[e.to],e.to));
            }
        }
    }
}

3.Dijkstra + DFS
输出最短路径

int G[maxn][maxn];
int d[maxn];
bool vis[maxn];
int optvalue;   //第二标尺最优值
vector<int> pre[maxn];  //存放结点的前驱结点
vector<int> path,tempPath;  //最优路径、临时路径
void Dijstra(int s)
{
    fill(d,d+maxn,inf);
    d[s] = 0;
    while(1){
        int u = -1;
        for(int v=1;v<=n;v++){
            if(!vis(v)&&(u==-1||d[u]>d[v])){
                u = v;
            }
        }
        if(u==-1) break;
        vis[u] = true;
        for(int v=1;v<=n;v++){
            if(!vis[v]&&G[u][v]!=inf){
                if(d[u]+G[u][v]<d[v]){
                    d[v] = d[u]+G[u][v];
                    pre[v].clear();
                    pre[v].push_back(u);
                }
                else if(d[u]+G[u][v]==d[v]){
                    pre[v].push_back(u);
                }
            }
        }
    }
}
void DFS(int v) //当前访问结点
{
    if(v == st){    //如果到达了叶子结点st(即路径起点)
        tempPath.push_back(v);  //将起点st加入临时路径tempPath的后面
        int value;
        计算路径tempPath上的value值
        if(value优于optvalue){
            optvalue = value;
            path = tempPath;
        }
        tempPath.pop_back();    //将刚加入的结点删除
        return ;
    }
    //递归式
    tempPath.push_back(v);
    for(int i=0;i<pre[v].size();i++)
        DFS(pre[v][i]);
    temPath.pop_back();
}
计算路径tempPath上的value值
if(value优于optvalue){
    optvalue = value;
    path = tempPath;
}
//边权之和
int value = 0;
for(int i=tempPath.size()-1;i>=0;i--){
    int id = tempPath[i];
    int idNext = tempPath[i-1];
    value += G[id][idNext];
}
//点权之和
int value = 0;
for(int i=tempPath.size()-1;i>=0;i--){
    int id = tempPath[i];
    value += w[id];
}

3.SPFA 期望时间复杂度O(kE),最差O(VE)

vector<node> Adj[maxn]; //图G的邻接表
int n,num[maxn];
bool vis[maxn];
bool SPFA(int s)
{
    for(int i=1;i<=n;i++){
        num[i] = 0;
        d[i] = inf;
        vis[i] = false;
    }
    queue<int> q;
    q.push(s);
    num[s]++;
    vis[s] = true;
    
    while(!q.empty()){
        int u = q.front();
        q.pop();
        vis[u] = false;
        for(int j=0;j<Adj[u].size();j++){
            int vv = Adj[u][j].v;
            int dd = Adj[u][j].d;
            if(d[u]+dd<d[vv]){
                d[vv] = d[u] + dd;
                if(!vis[vv]){
                    q.push(vv);
                    num[vv]++;
                    vis[vv] = true;
                    if(num[vv]>=n) return false;
                }
            }
        }
    }
    return true;
}

3.任意两点间的最短路问题
Floyd-Warshall O(|V|3)

int d[maxn][maxn];
int n;
void warshall_floyed(){
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                d[i][j] = min(d[i][j],d[i][k]+d[k][j]);
            }
        }
    }
}

4.路径还原
以Dijkstra为例

int pre[maxn],d[maxn];
bool vis[maxn];
int n;
void Dijkstra(int s)
{
    for(int i=1;i<=n;i++){
        pre[i] = -1;
        d[i] = inf;
        vis[i] = false;
    }
    d[s] = 0;
    while(1){
        int u = -1;
        for(int v=1;v<=n;v++){
            if(!vis[v]&&(u==-1||d[u]>d[v])){
                u = v;
            }
        }
        if(u==-1) break;
        vis[u] = true;
        for(int v=1;v<=n;v++){
            if(d[v]>d[u]+ Adj[u][v]){
                d[v] = d[u] + Adj[u][v];
                pre[v] = u;
            }
        }
    }
}
vector<int> get_path(int t)
{
    vector<int> path;
    for( ;t!=-1;t=pre[t]) path.push_back(t);    //得到的是t到s的顺序,翻转即可
    reverse(path.begin(),path.end());
    return path;
}

1、最小生成树问题1(Prim算法)
O(|V|^2) 堆维护O(|E|lon|V|)

int G[maxn][maxn];
int d[maxn];
bool vis[maxn];
int n;
int prim(int s)
{
    for(int i=1;i<=n;i++){
        d[i] = inf;
        vis[i] = false;
    }
    d[s] = 0;
    int res = 0;
    
    while(1){
        int u = -1;
        for(int v=1;v<=n;v++){
            if(!vis[v]&&(u==-1||d[u]>d[v])){
                u = v;
            }
        }
        if(u==-1) break;
        vis[u] = true;
        res += d[u];
        for(int v=1;v<=n;v++){
            if(!vis[v]&&d[v]>G[u][v]){
                d[v] = G[u][v];
            }
        }
    }
    return res;
}

2.最小生成树问题2(Krukal算法)
O(|E|long|V|)

struct edge{
    int u,v,cost;
};
bool cmp(const edge& a,const edge& b){
    return a.cost < b.cost;
}
edge E[maxn];
int father[maxn];
int findfather(int x)
{
    if(x!=father[x]){
        father[x] = findfather(father[x]);
    }
    return father[x];
}
int Kruskal(int n,int m)
{
    //ans为所求边权之和,Num_Edge为当前生成树的边数
    int ans = 0,Num_Edge = 0;
    for(int i=1;i<=n;i++)
        father[i] = i;
    sort(E+1,E+m+1,cmp);
    for(int i=1;i<=m;i++){
        int faU = findfather(E[i].u);
        int faV = findfather(E[i].v);
        if(faU!=faV){
            father[faU] = faV;
            ans += E[i].cost;
            Num_Edge++;
            if(Num_Edge==n-1) break;
        }
    }
    if(Num_Edge!=n-1) return -1;
    else return ans;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逃夭丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值