Graph Practise

border="0" width="330" height="86" src="//music.163.com/outchain/player?type=2&id=514765154&auto=1&height=66">

这里记录一下最近写的关于图的东西,图还是比较难的,这里有些可能不能用,但分开来都是对的

#pragma once
//带权
//此文件不能直接使用
#define MAXV 100
#define INF 0x3fffffff
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <utility>
#include <functional>
#include <algorithm>

struct Node
{
    int v, w;
    Node(int _v, int _w) :v(_v), w(_w) {}; //构造函数
};

std::vector<Node> Adj[MAXV];
int n;
bool vis[MAXV] = { false };
void DFS(int u, int depth) {
    vis[u] = true;
    printf("%d", u);
    for (int i = 0; i < Adj[u].size(); ++i) {
        int v_now = Adj[u][i].v;
        if (!vis[v_now])
            DFS(v_now, depth + 1);
    }
}

void DFSTrave() {
    for (int u = 0; u < n; ++u) {
        if (!vis[u])
            DFS(u, 1);
    }
}

bool inq[MAXV] = { false };
void BFS(int u) {
    std::queue<int> q;
    q.push(u);
    inq[u] = true;
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int i = 0; i < Adj[u].size(); ++i) {
            int v_now = Adj[u][i].v;
            if (!inq[v_now]) {
                q.push(v_now);
                inq[v_now] = true;
            }
        }
    }
}

void  BFSTrave() {
    for (int u = 0; u < n; ++u) {
        if (!inq[u]) BFS(u);
    }
}

//*********Dijkstra算法*************
//O(v^2+e),可以经最小堆优化
int n; //顶点数,MAXV为最大顶点数
int d[MAXV]; //起点到达各点的最短路径长度
bool vis2[MAXV] = { false }; //是否被访问
int pre[MAXV]; //存放前驱下标
void Dijkstra(int s) {
    //初始化最短路径为INF
    for (int i = 0; i < MAXV; ++i)
        d[i] = INF;
    for (int i = 0; i < MAXV; ++i)
        pre[i] = i;
    d[s] = 0; //起点至自身为0
    for (int i = 0; i < n; ++i) {
        //寻找最小值
        int u = -1, min = INF;
        for (int j = 0; j < n; ++j) {
            if (!vis[j] && d[j] < min) {
                u = j;
                min = d[j];
            }
        }
        if (u = -1) return; //找不到这样的点,即访问完毕
        vis[u] = true; //标记一下访问
        for (int j = 0; j < Adj[u].size(); ++j) {
            int now = Adj[u][j].v;
            if (!vis[now] && d[u] + Adj[u][j].w < d[now])
                pre[now] = u; //更新前驱
                d[now] = d[u] + Adj[u][j].w;
        }

    }
}

//*********Bellman-Ford算法*************
//O(ve)
bool Bellman(int s) {
    bool isOk = false;
    //初始化最短路径为INF
    for (int i = 0; i < MAXV; ++i)
        d[i] = INF;
    d[s] = 0; //起点至自身为0
    for (int i = 0; i < n - 1; ++i) {
        for (int u = 0; u < n; ++u) {
            for (int j = 0; j < Adj[u].size(); ++j) {
                int v = Adj[u][j].v;
                int w = Adj[u][j].w;
                if (d[u] + w < d[v]) {
                    d[v] = d[u] + w;
                    isOk = true;
                }
            }
        }
        if (isOk) isOk = false;
        else break; //已经没有松弛了
    }
    for (int u = 0; u < n; ++u) {
        for (int j = 0; j < Adj[u].size(); ++j) {
            int v = Adj[u][j].v;
            int w = Adj[u][j].w;
            if (d[u] + w < d[v]) {
                return false; //还存在松弛有负环
            }
        }
    }
    return true;

}

//*********SPFA算法---优化后的BF算法********
int num[MAXV]; //若明确无负环,可不要这一步
bool SPFA(int s) {
    memset(num, 0, sizeof(num));
    memset(inq, false, sizeof(inq));
    //初始化最短路径为INF
    for (int i = 0; i < MAXV; ++i)
        d[i] = INF;
    //源点入队
    std::queue<int> q;
    q.push(s);
    num[s]++;
    d[s] = 0;
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        inq[u] = false; //认为不在队列里面了
        for (int j = 0;j < Adj[u].size();++j) {
            int v = Adj[u][j].v;
            int w = Adj[u][j].w;
            if (d[u] + w < d[v]) {
                d[v] = d[u] + w; //还存在松弛
                if (!inq[v]) { //若v不在队列中
                    q.push(v);
                    inq[v] = true;
                    num[v]++;
                    if (num[v] >= n) return false; //有可达负环
                }
            }
        }
    }
    return true;
}

//*********Floyd算法************
//O(n^3),不超过200使用
int dis[MAXV][MAXV]; //只用邻接矩阵表示
void Floyd() {
    for (int k = 0; k < n; k++)
    {
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
            {
                if (dis[i][k] != INF && dis[k][j] != INF && dis[i][k] + dis[k][j] < dis[i][j])
                    dis[i][j] = dis[i][k] + dis[k][j];
            }
        }
    }
}

//*********prim算法************
typedef std::pair<int, int> P; //1:距离,2:编号
int ds[MAXV]; //顶点到集合s的距离
bool vis[MAXV] = { false }; //是否收录到集合s中

int prim() { //默认0点为起点
    for (int i = 0; i < n; ++i) ds[i] = INF;
    ds[0] = 0;
    int ans = 0; //最小生成树的边权之和
    std::priority_queue<P, std::vector<P>, std::greater<P> > q;
    q.push(P(ds[0], 0));
    while (!q.empty()) {
        int u;
        do
        {
            P p = q.top();
            q.pop();
            u = p.second;
        } while (vis[u]); //关键操作!
        //if (p.first > ds[u]) continue;
        vis[u] = true;
        ans += ds[u];
        for (int j = 0; j < Adj[u].size(); ++j) {
            int vnow = Adj[u][j].v;
            if (!vis[vnow] && Adj[u][j].w < ds[vnow]) {
                ds[vnow] = Adj[u][j].w;
                q.push(P(ds[vnow], vnow));
            }
        }
    }
    return ans;
}


//*********kruskal算法************
struct edge {
    int u, v;
    int cost; //边权
}E[MAXV];

bool cmp(edge &a, edge &b) {
    return a.cost < b.cost;
}

int father[MAXV]; //并查集!!!

int findFather(int x) {
    //很牛逼的路径压缩
    if (father[x] < 0) return x;
    else return father[x] = findFather(father[x]);
}

int kruskal(int n, int m) { //n为顶点数,m为边数
    int ans = 0, num_edge = 0; //边权和,边数
    memset(father, -1, sizeof(int)*n);
    std::sort(E, E + n, cmp); //这是很好的操作吗?
    //还可以用最小堆
    for (int i = 0; i < n; ++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;
}

//*********拓扑排序************
int inDegree[MAXV];
bool topological_sort() {
    int num = 0;
    std::queue<int> q;
    for (int i = 0; i < n; ++i) {
        if (!inDegree[i])
            q.push(i); //入度为0入队列
    }
    while (!q.empty()) {
        int u = q.front();
        printf("%d", u);
        q.pop();
        for (int i = 0; i < Adj[u].size(); ++i) {
            int v = Adj[u][i].v;
            inDegree[v]--; //后继节点入度减一
            if (!inDegree[v])
                q.push(v);
        }
        Adj[u].clear();
        num++;
    }
    if (num == n) return true;
    else return false;
}

//*********关键活动************
#include <stack>
//有点难= =
int ve[MAXV];
int vl[MAXV];
std::stack<int> topOrder; //供逆拓扑使用
bool topological_sort2() {
    std::queue<int> q;
    for (int i = 0; i < n; ++i) {
        if (inDegree[i] == 0)
            q.push(i);
    }
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        topOrder.push(u);
        for (int i = 0; i < Adj[u].size(); ++i) {
            int v = Adj[u][i].v;
            if (ve[u] + Adj[u][i].w > ve[v])
                ve[v] = ve[u] + Adj[u][i].w;
        }
    }
    if (topOrder.size() == n) return true;
    else return false;
}

int CriticalPath() {
    memset(ve, 0, sizeof(ve));
    if (!topological_sort2()) return -1;
    for (int i = 0; i < n; ++i) {
        vl[i] = ve[n - 1];
    }

    //逆拓扑序列
    while (!topOrder.empty()) {
        int u = topOrder.top();
        topOrder.pop();
        for (int i = 0; i < Adj[u].size(); ++i) {
            int v = Adj[u][i].v;
            if (vl[v] - Adj[u][i].w < vl[u])
                vl[u] = vl[v] - Adj[u][i].w;
        }
    }

    for (int u = 0; u < n; ++u) {
        for (int i = 0; i < Adj[u].size(); ++i) {
            int v = Adj[u][i].v, w = Adj[u][i].w;
            int e = ve[u], l = vl[u] - w;
            if (e == l)
                printf("%d->%d", u, v);
        }
    }
    return ve[n - 1]; //返回关键路径长度
}

priority_queue那块写繁了,可以不用pair

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值