zoukankan      html  css  js  c++  java
  • 算法笔记(图专题)

    1.图的DFS

    //伪代码(通用)
    DFS(u){//访问顶点u
        vis[u] = true;   //设置u已被访问
        for(从u出发能到达的所有顶点v){
            if(vis[v] == false)  //如果v未被访问
                DFS(v);  //递归访问v
        }
    }
    DFSTrave(G){ //遍历图G
        for(G的所有顶点){  
            if(vis[u] == false) //如果u未被访问
                DFS(u);  //访问u所在的连通块
        }
    }

    邻接矩阵版:

    const int MAXV = 1000;
    const int INF = 10000000; //G[i][j]设为INF表示两点间不通
    int n, G[MAXV][MAXV];  //n为顶点数,MAXV为最大顶点数
    bool vis[MAXV] = {false};   //记录顶点i是否已被访问
    void DFS(int u, int depth){ //u为当前访问的顶点标号,depth为深度
        vis[u] = true; //设置u已被访问
        //此处可添加一些对u的操作
        for(int v=0; v<n; v++){ //对每个顶点v
            if(vis[v]==false && G[u][v]!=INF){  //如果v未被访问,且u可到达v
                DFS(v, depth+1);  //访问v,深度加1
            }
        }
    }
    void DFSTrave(){  //遍历图G
        for(int u=0; u<n; u++){ //对每个顶点u
            if(vis[u] == false){  //如果u未被访问
                DFS(u, 1);  //访问u和u所在的连通块,1表示初始为第一层
            }
        }
    }

    领接表版:

    vector<int> Adj[MAXV];  //图G的领接表
    int n;  //n为顶点数,MAXV为最大顶点数
    bool vis[MAXV] = {false};   //记录顶点i是否已被访问
    void DFS(int u, int depth){
        vis[u] = true;
        for(int i=0; i<Adj[u].size(); i++){
            int v = Adj[u][i];
            if(vis[v] == false){
                DFS(v, depth+1);
            }
        }
    }
    void DFSTrave(){
        for(int u=0; u<n; u++){ //对每个顶点u
            if(vis[u] == false){  //如果u未被访问
                DFS(u, 1);  //访问u和u所在的连通块,1表示初始为第一层
            }
        }
    }

    2.图的BFS

    邻接矩阵版:

    int n, G[MAXV][MAXV];  //n为顶点数,MAXV为最大顶点数
    bool inq[MAXV] = {false};
    void BFS(int u){
        queue<int> q;
        q.push(u);   //将初始点u入队
        while(!q.empty()){  //只要队列非空
            int u = q.front(); //去处队首元素
            q.pop();
            for(int v=0; v<n; v++){
                if(inq[v]==false && G[u][v]!=INF){
                    q.push(v); //将v入队
                    inq[v] = true;
                }
            }
        }
    }
    void BFSTrave(){
        for(int u=0; u<n; u++){
            if(inq[u] == false){
                BFS(q);
            }
        }
    }

    领接表版:

    vector<int> Adj[MAXV];
    int n;
    bool inq[MAXV] = {false};
    void BFS(int u){
        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 = Adj[u][i];  //结点u的相邻结点
                if(inq[v] == false){
                    q.push(v);
                    inq[v] = true;
                }
            }
        }
    }
    void BFSTrave(){
        for(int u=0; u<n; u++){
            if(inq[u] == false){
                BFS(q);
            }
        }
    }
    //带顶点层号的
    struct Node{
        int v;  //顶点编号
        int layer;  //顶点层号
    };
    vector<Node> Adj[N];
    void BFS(int s){  //s为起始点编号
        queue<Node> q;
        Node start;
        start.v = s;  //起始顶点编号
        start.layer = 0;  //起始顶点层号为0
        q.push(start);
        inq[start.v] = true;
        while (!q.empty()){
            Node topNode = q.front();  //取出队首元素
            q.pop();
            int u = topNode.v;   //队首顶点的编号
            for(int i=0; i<Ajd[u].size(); i++){
                Node next = Adj[u][i];   
                next.layer = topNode.layer + 1;    //next层号等于当前顶点层号+1
                if(inq[next.v] == false){  //如果next的编号未被加入过队列
                    q.push(next);
                    inq[next.v] = true;
                }
            }
        }
    }

    3.单源最短路径

    Dijkstra (适合无负权路径)

    两个关键:

    • 集合S存放已被访问的结点,用 bool vis[] 来实现

    • int d[] 表示起点 s 到达顶点 Vi 的最短距离

    //伪代码
    //G为图,一般为全局变量,数组d为源点到达各顶点的最短路径长度, s为起点
    Dijkstra(G[][], d[], s){
        初始化;  //(勿忘)
        for(循环n次){
            u = 使d[u]最小的还未被访问的顶点的标号;
            记u已被访问;
            for(从u出发能到达的所有顶点v){
                if(v未被访问 && 以u为中介点使s到顶点v的最短距离d[v]更优){
                    优化d[v];
                }
            }
        }
    }

    邻接矩阵版:

    const int INF = 10000000; //起点到达各点的最短路径长度
    bool vis[MAXV] = {fasle};  //标记数组
    int pre[MAXV];  //pre[v]表示起点到顶点v的最短路径v的前一个顶点(*****新增加******)
    void Dijkstra(int s){ //s为起点
        fill(d, d+MAXV, INF);
        for(int i=0; i<n; i++) pre[i] = i;  //pre[]初试状态设每个结点的前驱为自身(*****新增加******)
        d[s] = 0;  //起点s到达自身的距离为0
        for(int i=0; i<n; i++){  //循环n次
            int u = -1, MIN = INF;  //u使d[u]最小,MIN存放该最小的d[u]
            for(int j=0; j<n; j++){  //找到未访问的顶点中d[]最小的
                if(vis[j] == false && d[j] < MIN){
                    u = j;
                    MIN = d[j];
                }
            }
            //找不到小于INF的d[u],说明剩下的顶点和起点s不连通
            if(u == -1) return;
            vis[u] = true;  //标记u已访问
            for(int v=0; v<n; v++){  //(此处可添加应用扩展)
                //如果v未访问 && u能到达v && 以u为中间点可以使d[v]更优
                if(vis[v] == false && G[u][v]!=INF && d[u]+G[u][v] < d[v]){
                    d[v] = d[u] + G[u][v]; //优化d[v]
                    pre[v] = u;  //记录v的前驱顶点是u(*****新增加******)
                }
            }
        }
    }
    --------------------------------------------------------------------------------------------------
    //应用扩展
    //(1)新增边权,用cost[u][v]表示u->v的花费(由题目输入),增加数组c[]表示从起点s到达u的最小花费c[u]
    //初始化c[s]为0, 其余为INF
    for(int v=0; v<n; v++){
        if(vis[v] == false && G[u][v] != INF){
            if(d[u]+G[u][v] < d[v]){ //以u为中间点可以使d[v]更优
                d[v] = d[u] + G[u][v];
                c[v] = c[u] + cost[u][v];
            } else if(d[u]+G[u][v] == d[v] && c[u]+cost[u][v] < c[v]){
                c[v] = c[u] + cost[u][v]; //最短距离相同时看能否使c[v]更优
            }
        }
    }
    //新增点权,weight[u]表示点u的点权值,并增加一个数组w[],令从起点s到达u的点权值总和为w[u]
    //初始化w[s]为weight[s]、其余w[u]均为0
    for(int v=0; v<n; v++){
        if(vis[v] == false && G[u][v]!=INF){
            if(d[u]+G[u][v] < d[v]){  //以u为中介点可以使d[v]更优
                d[v] = d[u] + G[u][v];
                w[v] = w[u] + weight[v];
            } else if(d[u]+G[u][v] == d[v] && w[u]+weigh[v] > w[v]){ //(此处可根据需求修改>或<)
                w[v] = w[u] + weight[v]; //最短距离相同时看能否使w[v]更优
            }
        }
    }
    //求最短路径条数,只需增加一个数组num[],令从起点s到达顶点u的最短路径条数为num[u],
    //初始化时只有num[s]为1、其余num[u]均为0
    for(int v=0; v<n; v++){
        if(vis[v] == false && G[u][v]!=INF){
            if(d[u]+G[u][v] < d[v]){  //以u为中介点可以使d[v]更优
                d[v] = d[u] + G[u][v];
                num[v] = nun[u];  //num[v] 继承 nun[u]
            } else if(d[u]+G[u][v] == d[v]){ //(此处可根据需求修改>或<)
               num[v] += num[u]; //最短距离相同时累加num******
            }
        }
    }

    Bellman-Ford算法:(能处理带零环或正环的图)

    需要遍历所有边,使用领接表更方便

    struct Node{
        int v, dis;  //v为领接表的目标顶点,dis为领接表的边权
    }
    vector<Node> Adj[MAXV];  //图G的领接表
    int n, d[MAXV];  //n为顶点,d[]为起点到各点的最短路径长度
    bool Bellman(int s){ //s为源点
        fill(d, d+MAXV, INF);  //初始化d
        d[s] = 0;
        //以下为求解数组d的部分
        for(int i=0; i<n-1; i++){  //执行n-1轮操作,n为顶点数
            for(int u=0; u<n; u++){  //每轮操作都遍历所有边
                for(int j=0; j<Adj[u].size(); j++){
                    int v = Adj[u][j].v;  //领接边的顶点
                    int dis = Adj[u][j].dis;  //领接边的边权
                    if(d[u] + dis < d[v]){   //以u为中介点可以使d[v]更小
                        d[v] = d[u] + dis;  //松弛操作
                    }
                }
            }
        }
        //以下判断负环的代码
        for(int u=0; u<n; u++){//对每条边进行判断
            for(int j=0; j<Adj[u].size(); j++){
                int v = Adj[u][j].v;  //邻接边的顶点
                int dis = Adj[u][j].dis;  //邻接边的边权
                if(d[u]+dia < d[v]){ //如果仍可以被松弛
                    return false;  //说明图中有从源点可达的负环
                }
            }
        }
        return true;  //数组d的所有值都已经到达最优
    }

    4.全源最短路径

    Floyd算法:

    • 时复O(n^3),n控制在200以内。

    • 用邻接矩阵。

    //伪代码
    枚举顶点 k ∈ [1, n]
        以顶点k作为中介点,枚举所有顶点对i和j (i∈[1,n], j∈[1,n])
            如果dis[i][k] + dis[k][j] < dis[i][j]成立
                赋值dis[i][j] = dis[i][k] + dis[k][j]
    const int INF = 10000000;
    const int MAXV = 200;
    int n, m;  //n为顶点数,m为边数
    int dis[MAXV][MAXV];  //dis[i][j]表示顶点i和顶点j的最短距离
    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];  //找到更短的路径
                    }
                }
            }
        }
    }
    //最后输出dis[][]数组

    5.最小生成树

    • 最小生成树是树,因此其边数等于顶点数减1,且树内一定不会有环;

    • 对给定的图G(V,E),其最小生成树可以不唯一,但其边权之和一定是唯一的;

    • 由于最小生成树是在无向图上生成的,因此其根结点可以是这棵树上的任意一个结点。

    Prim算法:

    //伪代码
    Prime(G, d[]){
        初始化;
        for(循环n次){
            u = 使d[u]最小的还未被访问的顶点的标号;
            记u已被访问;
            for(从u出发能到达的所有顶点){
                if(v未被访问 && 以u为中介点使得v与集合S的最短距离d[v]更优){
                    将G[u][v]赋值给v与集合的最短距离d[v];
                }
            }
        }
    }

    邻接矩阵版:

    int n, G[MAXV][MAXV];  //n为顶点数
    int d[MAXV];  //*****顶点与集合S的最短距离*****
    bool vis[MAXV] = {false}; //标记数组
    int prime(){ //默认0为初始点,函数返回最小生成树的边权之和
        fill(d, d+MAXV, INF);
        d[0] = 0;  //只有0号顶点到集合S的距离为0,其余全为INF
        int ans = 0;  //存放最小生成树的边权之和
        for(int i=0; i<n; i++){
            int u = -1, MIN = INF;
            for(int j=0; j<n; j++){
                if(vis[j] == false && d[j] < MIN){
                    u = j;
                    MIN = d[j];
                }
            }
            if(u == -1) return -1;//找不到小于INF的d[u],则剩下的顶点和集合S不连通
            vis[u] = true;
            ans += d[u];   //*****将与集合S最小的边加入最小生成树*****
            for(int v=0; v<n; v++){
                if(vis[v] == false && G[u][v]!=INF && G[u][v] < d[v]){
                    d[v] = G[u][v];  //(***注意这里与Dijkstra的区别***)
                }
            }
        }
        return ans;
    }
  • 相关阅读:
    ubuntu 安装 redis desktop manager
    ubuntu 升级内核
    Ubuntu 内核升级,导致无法正常启动
    spring mvc 上传文件,但是接收到文件后发现文件变大,且文件打不开(multipartfile)
    angular5 open modal
    POJ 1426 Find the Multiple(二维DP)
    POJ 3093 Margritas
    POJ 3260 The Fewest Coins
    POJ 1837 Balance(二维DP)
    POJ 1337 A Lazy Worker
  • 原文地址:https://www.cnblogs.com/dear_diary/p/8616235.html
Copyright © 2011-2022 走看看