图算法小结

来源:互联网 发布:关于初中语文的软件 编辑:程序博客网 时间:2024/06/08 09:09

本目总结常见的图算法。

kruskal

构造mst,按边从小到大遍历,如果边的响铃节点位于不同的集合,那么该边是mst当中的一条边。这个地方需要并查集的知识。

  • 题目:[jobdu-1017]
  • 代码
#include <iostream>#include <vector>#include <algorithm>#define N 100int tree[N + 8];struct Edge {    int u;    int v;    int w;    Edge() {}    Edge( int uu, int vv, int ww ) : u(uu), v(vv), w(ww) {}    bool operator<( const Edge& rhs ) const {        return w< rhs.w;    }};typedef std::vector<Edge> EdgeList;int find_root(int x){    if( tree[x] == -1 ) return x;    else{        int tmp = find_root( tree[x] );        tree[x] = tmp;        return tmp;    }}int main( void ){    int n = 0;    while( std::cin >> n, n ){        EdgeList edge_list;        for( int i = 1; i <= n; ++i ) tree[i] = -1;        for( int i = 0; i < n*(n-1)/2; ++i ){            int u, v, w;            std::cin >> u >> v >> w;            Edge edge(u, v, w);            edge_list.push_back(edge);        }        std::sort( edge_list.begin(), edge_list.end() );        int ans = 0;        int n = edge_list.size();        for(int i = 0; i < n; ++i){            int uu = edge_list[i].u;            int vv = edge_list[i].v;            int ww = edge_list[i].w;            int a = find_root(uu);            int b = find_root(vv);            if( a == b ) continue;            else{                ans += ww;                tree[a] = b;            }        }        std::cout << ans << std::endl;    }    return 0;}

dijkstra

基于路径长度依次递增,所以w > 0

  • 题目:[jobdu-1447]
  • 代码
#include <iostream>#include <vector>#include <limits.h>#define N 100struct Edge {    int node;    int weight;    Edge() {}    Edge( int n, int w ) : node(n), weight(w) {}};typedef std::vector<Edge> EdgeList;EdgeList adj_list[N + 8];int d[N+8];int flag[N+8];int dijk( int n, int s, int t );void relax( int u, int v, int w );int main( void ){    int n,m;    while(std::cin >> n >> m){        if( !n && !m ) break;        for( int i = 1; i <= n; ++i ) adj_list[i].clear();        for( int i = 0; i < m; ++i ){            int u,v,w;            std::cin >> u >> v >> w;            Edge edge(v,w);            adj_list[u].push_back(edge);            Edge edge1(u,w);            adj_list[v].push_back(edge1);        }        int ans = dijk( n, 1, n );        std::cout << ans << std::endl;    }    return 0;}int dijk( int n, int s, int t ){    for( int i = 1; i <= n; ++i ){        d[i] = INT_MAX;    }    d[s] = 0;    for( int i = 1; i <= n; ++i ) flag[i] = 0;    int cnt = n;    while(cnt--){        int u;        int min = INT_MAX;        for( int i = 1; i <= n; ++i ){            if( flag[i] ) continue;            if( d[i] < min ){                u = i;                min = d[i];            }        }        flag[u] = 1;        if( u == t ) break;        int sz = adj_list[u].size();        for( int k = 0; k < sz; ++k ){            int v = adj_list[u][k].node;            int w = adj_list[u][k].weight;            relax(u, v, w);        }    }    return d[t];}void relax( int u, int v, int w ){    if( d[u] + w < d[v] )        d[v] = d[u] + w;}

bellman-ford

需要注意的是,bellman-ford的基本思路是按照层次依次松弛的做法。而dijk则是按照路径长度依次递增。整体思路不同。
bellman-ford之所以要松弛V-1次,原因在于,图最长的一条路径是他退化成链表的情形。此时N个顶点的图,最长的边为N-1。每次,松弛所有边。
如果,松弛完所有层次之后。
再松弛依次发现还可以松弛,那么证明,这个图当中存在负边。

  • 题目:同上
  • 代码
#include <iostream>#include <vector>#include <limits.h>#define N 100struct Edge {    int node;    int weight;    Edge() {}    Edge( int n, int w ) : node(n), weight(w) {}};typedef std::vector<Edge> EdgeList;EdgeList adj_list[N + 8];int d[N+8];int bellman_ford( int n, int s, int t );void relax( int u, int v, int w );int main( void ){    int n,m;    while(std::cin >> n >> m){        if( !n && !m ) break;        for( int i = 1; i <= n; ++i ) adj_list[i].clear();        for( int i = 0; i < m; ++i ){            int u,v,w;            std::cin >> u >> v >> w;            Edge edge(v,w);            adj_list[u].push_back(edge);            Edge edge1(u,w);            adj_list[v].push_back(edge1);        }        int ans = bellman_ford( n, 1, n );        std::cout << ans << std::endl;    }    return 0;}int bellman_ford( int n, int s, int t ){    for(int i = 1; i <= n; ++i) d[i] = INT_MAX;    d[s] = 0;    for( int cnt = 0; cnt < n-1; ++cnt ){ // V-1         // relax every edge        for( int u = 1; u <= n; ++u ){            int sz = adj_list[u].size();            for( int k = 0; k < sz; ++k ){                int v = adj_list[u][k].node;                int w = adj_list[u][k].weight;                relax(u, v, w);            }        }    }    for( int u = 1; u <= n; ++u ){        int sz = adj_list[u].size();        for( int k = 0; k < sz; ++k ){            int v = adj_list[u][k].node;            int w = adj_list[u][k].weight;            if( d[u] + w < d[v] )                return -1;        }    }    return d[t];}void relax( int u, int v, int w ){    if( d[u] + w < d[v] )        d[v] = d[u] + w;}

spfa

大体的思想来自于bellman-ford,但是,每次只是松弛有效的边,不用松弛所有的边。这是他的主要思想。
增加了两个数据结构:
visited[v],表示v这个节点是否在队列中
count[v],表示v的入队次数,如果超过N-1次。存在负边,比如只有两个顶点的图,存在两条边,有一条是负的。存在这种情形。

#include <iostream>#include <vector>#include <limits.h>#include <queue>#define N 100struct Edge {    int node;    int weight;    Edge() {}    Edge( int n, int w ) : node(n), weight(w) {}};typedef std::vector<Edge> EdgeList;EdgeList adj_list[N + 8];int d[N+8];int visited[N+8]; // in the queue or notint count[N+8];int spfa( int n, int s, int t );int main( void ){    int n,m;    while(std::cin >> n >> m){        if( !n && !m ) break;        for( int i = 1; i <= n; ++i ) adj_list[i].clear();        for( int i = 0; i < m; ++i ){            int u,v,w;            std::cin >> u >> v >> w;            Edge edge(v,w);            adj_list[u].push_back(edge);            Edge edge1(u,w);            adj_list[v].push_back(edge1);        }        int ans = spfa( n, 1, n );        std::cout << ans << std::endl;    }    return 0;}int spfa( int n, int s, int t ){    for(int i = 1; i <= n; ++i) d[i] = INT_MAX;    d[s] = 0;    for( int i = 1; i <= n; ++i ){ visited[i] = 0; count[i] = 0; }    std::queue<int> que;    que.push(s);    visited[s] = 1;    count[s] += 1;    while( !que.empty() ){        int u = que.front();        que.pop();        visited[u] = 0;        int sz = adj_list[u].size();        for( int k = 0; k < sz; ++k ){            int v = adj_list[u][k].node;            int w = adj_list[u][k].weight;            if( d[u] + w < d[v] ){                d[v] = d[u] + w;                if( visited[v] == 0 ){                    que.push(v);                    visited[v] = 1;                    count[v] += 1;                    if( count[v] > n-1 )                        return -1;                }            }        }    }    return d[t];}

topsort

拓扑排序是这样一种顶点序列,如果vi到vj存在一条边,那么vi排在vj的前面。具体的做法是每次寻找度为0的点,然后删除这些点所指向的边。重复n次或者指导没有度为0的点可以删除。

拓扑排序之所以可能存在多种的情形在于,度为0的点选择不同。

  • 题目: [jobdu-1448]
  • 代码
#include <iostream>#include <vector>#include <queue>#define N 100typedef std::vector<int> EdgeList;EdgeList adj_list[N + 8];int indegree[N + 8];int main( void ){    int n, m;    while( std::cin >> n >> m ){        if( !n && !m ) break;        for( int i = 0; i < n; ++i ){ adj_list[i].clear(); indegree[i] = 0; }        for( int i = 0; i < m; ++i ){            int u,v;            std::cin >> u >> v;            adj_list[u].push_back(v);            ++indegree[v];        }        std::queue<int> q;        for( int i = 0; i < n; ++i ){            if( !indegree[i] )                q.push(i);        }        int cnt = 0;        while(!q.empty()){            int u = q.front();            q.pop();            ++cnt;            int sz = adj_list[u].size();            for( int k = 0; k < sz; ++k ){                int v = adj_list[u][k];                --indegree[v];                if( !indegree[v] ) q.push(v);            }        }        if( cnt == n ) std::cout << "YES" << std::endl;        else std::cout << "NO" << std::endl;    }    return 0;}
原创粉丝点击