Graph Theory Study Notes

Graph Theory Study Notes

Recently this review konjac in graph theory, found himself the basic graph theory to forget, so I intend to write such a study notes for review.

A, represented in FIG.

1. adjacency matrix

With a two-dimensional memory array side, \ (Map [I] [J] = K \) represents the point \ (I \) point \ (J \) weight value \ (K \) . Tim sides, check both sides of the complexity of O (1), but take up too much space.

template:

int map[5000][5000],u,v,k,n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
    scanf("%d%d%d",&u,&v,&k);
    map[u][v]=k;
}

2. adjacency list

With \ (n-\) sides of each array of storage node sent, a smaller footprint.

Template (vector method):

#include<bits/stdc++.h>
using namespace std;
struct node{
    int v,k;
};
vector <node> head[5000];
int main(){
    int n,m,u,v,k;
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++){
        scanf("%d%d%d",&u,&v,&k);
        node a;
        a.v=v;a.k=k;
        head[u].push_back(a);
    }
    return 0;
}

Second, topological sorting

See this blog

Third, the minimum spanning tree

1. Definitions

A minimum spanning tree communication undirected graph is a subgraph of the figure, and is a tree, comprising all of the vertices of the figure, and the right side is minimum.

2.Prim algorithm

Provided a set of points \ (Q \) contains only the initial node (which may be any node), looking for an edge \ ((U, V) \) , satisfies point \ (U \) in the collection \ (Q \) , the point \ (V \) not in the set \ (Q \) , and the right side is the smallest of all such edges, then this edge is an edge in the minimum spanning tree, the point \ (V \) is also added a set of \ (Q \) in such operations are repeated until the set \ (Q \) contains all nodes. Then the set of all sides is to find the minimum spanning tree of this figure.

Figuratively speaking, that is, choose a point on the map, and then draw a circle around this point. (As)

enter image description here

Selected from the side and then the inside of a circle intersecting the minimum weight (i.e., between the edges 5 and 6 in the figure), then the minimum weight around the edge node is also directed to the circle. (As)

enter image description here

Then find and circle the right side of the intersection of the smallest edge, and the node pointed to the circle inside circle, and so on until the circle contains all the nodes, then all Sorted side is the minimum spanning tree .

template:

#include<bits/stdc++.h>
using namespace std;
const int INF=1e9;
struct node{
    int v,k;    
};
vector <node> head[5000];

int p[5000],q[5000],n,m,tot=0;//p[i]表示节点i与点集q中一点相连的最短的边的权值(好绕啊),q[i]表示点i是否在点集q中 
void Prim(int root){
    p[root]=0;
    for(int i=1;i<=n;i++){
        int Min=INF,u;
        for(int j=1;j<=n;j++){
            if(Min>p[j]){
                Min=p[j];
                u=j;
            }
        }//找最短边,此处可以用堆优化
        
        tot+=p[u];
        q[u]=0;
        p[u]=INF;
        for(int j=0;j<head[u].size();j++){
            int v=head[u][j].v,k=head[u][j].k;
            if(q[v]&&p[v]>k){
                p[v]=k;
            }
        }//更新p数组 
    }
}

int main(){
    scanf("%d%d",&n,&m);
    int x,y,z;node a;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&z);
        a.v=y;a.k=z;
        head[x].push_back(a);
        a.v=x;
        head[y].push_back(a);
    }
    for(int i=1;i<=n;i++){
        p[i]=INF;
        q[i]=1;
    }//初始化 
    Prim(1);
    printf("%d",tot);
    return 0;
}

3.Kruskal algorithm

First of all nodes into different points of focus, focus on only one node at each point. Then is traversed to find an edge \ ((U, V) \) , nodes that satisfy \ (U \) and the node \ (V \) not in the same set, and is the smallest satisfies this condition the edge weights. Then this edge is an edge in the minimum spanning tree. The node \ (u \) and node \ (v \) the set where the merger, before proceeding to find the edge and merge collections, a collection of all sides until all the points are in the same set, then find is this minimum spanning tree of FIG.

Kruskal's algorithm can take advantage of disjoint-set to achieve.

Figuratively speaking, that is, in view of all the surrounding nodes draw a circle (as shown):

enter image description here

Then the edge is not contained in a ring (i.e. the ring has two sides intersection) find the smallest edge weight values ​​(i.e., the edge of FIG. 5-6), these two combined rings. (As)

enter image description here

Then repeat this operation until all nodes in a circle, then all Sorted side is the minimum spanning tree.

template:

#include<bits/stdc++.h>
using namespace std;
const int INF=1e9;
int n,m,pre[200001],tot=0,k=0;

struct node{
    int u,v,k;
}edge[200001];//edge数组存边 

//----------------------------------------并查集 
inline int find(int x){
    int y=x,j;
    while(x!=pre[x]){
        x=pre[x];
    }
    while(y!=x){
        j=pre[y];
        pre[y]=x;
        y=j;
    }
    return x;
}

inline void join(int x,int y){
    int pre_x=find(x),pre_y=find(y);
    if(pre_x!=pre_y){
        pre[pre_x]=pre_y;
    }
    return;
}
//----------------------------------------并查集 

bool cmp(node x,node y){
    return x.k<y.k;
}

void Kruskal(){
    sort(edge,edge+m,cmp);//将边按权值从小到大排序 
    int cnt=0;
    for(int i=0;i<m;i++){
        if(cnt==n-1){
            return;
        }//边够了 
        int u=edge[i].u,v=edge[i].v,k=edge[i].k;
        if(find(u)!=find(v)){//如果这条边可以选 
            join(u,v);
            cnt++;
            tot+=k;
        }
    }
}

int main(){
    scanf("%d%d",&n,&m);
    int x,y,z;
    node a;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&z);
        edge[k].u=x;edge[k].v=y;edge[k].k=z;
        k++;
    }//edge数组存边 
    for(int i=1;i<=n;i++){
        pre[i]=i;
    }//并查集初始化 
    Kruskal();
    printf("%d",tot);
    return 0;
}

4.Prim difference algorithm and Kruskal's algorithm

algorithm Prim Kruskal
time complexity \ (O (v * log (v)) \) \ ((E * log (E)) \)
Scope Dense graph Sparse graph

Note: \ (V \) in dots, \ (E \) is the number of edges.
After Prim algorithm needs by optimizing the complexity of the heap in order to achieve \ (O (log v * (v)) \) .

Fourth, the shortest

1. Shortest Path Problem

最短路问题(short-path problem)是网络理论解决的典型问题之一,可用来解决管路铺设、线路安装、厂区布局和设备更新等实际问题。基本内容是:若网络中的每条边都有一个数值(长度、成本、时间等),则找出两节点(通常是源节点和阱节点)之间总权和最小的路径就是最短路问题。——百度百科

说人话就是在选一条连接两个点的路径,使这条路权值和最小。

2.Dijkstra算法

Dijkstra算法是一个求单源最短路径的算法,只适用于没有负权边的图。Dijkstra算法与Prim算法有些类似,都是设一个点集Q,最初只有一个节点,然后不断地添点,直到全部添加。

设点集Q,最初只包含起点\(u\)。设\(dis[i]\)\(u\)点到编号为\(i\)的节点的最短路,所有\(dis[i]\)初始化为INF,\(dis[u]\)初始化为0。首先寻找不在点集中且dis值最小的点\(x\)
,将点\(x\)加入Q中,并进行松弛操作,即对于所有从\(x\)点出发的边\((x,y)\)\(dis[y]=max(dis[y],dis[x]+w(x,y))\).然后再进行找点,加点与松弛的操作,直到所有点都被加入Q中为止。那么最短路就求出来了。

原理是由于图中没有负边,所以如果\(dis[i]\)为图中最小的,那么它就不可能再被更新,所以就可以被确定,即被加入点集Q中。

如果需要记录路径,那么就建立一个\(father[n]\)数组,\(father[i]\)表示\(i\)节点的上一个节点。每次进行松弛操作时,如果\(dis[i]>dis[j]+w(i,j)\),那么就将\(father[i]\)赋值为\(j\)即可。

与Prim算法相似,Dijkstra算法也可以在找\(dis\)值最大的节点时利用堆进行优化。

模板:

#include<bits/stdc++.h>
using namespace std;
const int INF=1e9;
struct edge{
    int v,k;
};
vector <edge> head[100001];

int dis[100001],q[100001];

struct node{
    int dis,u;//u:节点编号 dis:dis值 
    bool operator<(const node& a) const{//运算符重载,以使用STL优先队列 
        return dis>a.dis;
    }
}; 

int n,m;
void Dijkstra(int s){
    priority_queue <node> que;//堆优化 
    bool vis[100001]={0};//是否被访问过 
    for(int i=1;i<=n;i++){
        dis[i]=INF;
    }
    dis[s]=0;
    que.push((node){0,s});
    while(!que.empty()){
        int u=que.top().u;
        que.pop();
        if(vis[u]){//如果已经被访问过了,那么这个节点的dis值就是在松弛操作之前的dis值了,直接删除即可。
        //由于松弛后的dis值一定小于松弛前的dis值,所以不用担心出错。 
            continue;
        }
        vis[u]=1;
        q[u]=1;
        for(int i=0;i<head[u].size();i++){
            int v=head[u][i].v,k=head[u][i].k;
            if(!q[v]&&dis[v]>dis[u]+k){//松弛操作 
                dis[v]=dis[u]+k;
                que.push((node){dis[v],v});
            }
        }
    }
}

int main(){
    int u,v,k,s;
    scanf("%d%d%d",&n,&m,&s);
    
    for(int i=0;i<m;i++){
        scanf("%d%d%d",&u,&v,&k);
        edge a;
        a.v=v;a.k=k;
        head[u].push_back(a);
    }
    Dijkstra(s);
    for(int i=1;i<=n;i++){
        printf("%d ",dis[i]);
    }
    return 0;
}

3.Bellman-Ford算法

Bellman-Ford算法可以求有负权边的图的单源最短路径。算法是对\(dis\)数组进行\(n- 1\)(n为节点个数)轮松弛操作。

原理是最短路一定经过\(n-1\)个节点(起点不算),即有\(n-1\)\(dis\)值需要更新。每更新一个节点的\(dis\)值,其他的节点也可能会受到影响。故进行\(n-1\)轮松弛操作后可以保证求出最短路。

模板:

#include<bits/stdc++.h>
using namespace std;
const int INF=1e9;
struct node{
    int u,v,k;
};
vector <node> edge;

int dis[100001];

int n,m;
void Bellman_Ford(int s){
    for(int i=1;i<=n;i++){
        dis[i]=INF;
    }
    dis[s]=0;
    for(int i=0;i<n-1;i++){
        for(int j=0;j<m;j++){
            int u=edge[j].u,v=edge[j].v,k=edge[j].k;//遍历每一条边,进行松弛 
            if(dis[v]>dis[u]+k){
                dis[v]=dis[u]+k;
            }
        }
    }
}

int main(){
    int u,v,k,s;
    scanf("%d%d%d",&n,&m,&s);
    
    for(int i=0;i<m;i++){
        scanf("%d%d%d",&u,&v,&k);
        edge.push_back((node){u,v,k});//edge数组存边 
    }
    Bellman_Ford(s);
    for(int i=1;i<=n;i++){
        printf("%d ",dis[i]);
    }
    return 0;
}

另外值得一提的是Bellman-Ford算法还可以判断图中是否存在负环(即权值和为负的环),只需要在进行n-1次操作后在进行一轮松弛操作,如果还有节点的\(dis\)值被更新,说明图中存在负环。

4.SPFA算法

它不是死了吗
Shortest Path Fast Algorithm 算法是Bellman-Ford算法的队列优化版本。

我们可以发现,如果一个点的\(dis\)值更新了 ,那么与这个节点相邻的节点的\(dis\)值都可能会受到影响。所以我们可以建一个队列,存放等待更新的节点,并每次对队首的节点进行松弛操作并把所有与队首相连的节点入队,并把队首pop出去,重复进行这样的操作,直到队列为空。

模板:

#include<bits/stdc++.h>
using namespace std;
const int INF=10e9;
int n,m,dis[100001];
struct node{
    int v,k;
};
vector<node>head[100001];

void SPFA(int s){
    bool vis[100001];//vis数组记录是否在队列中 
    queue<int> que;
    que.push(s);
    for(int i=0;i<=n;i++){
        dis[i]=INF;
        vis[i]=0;
    }
    vis[s]=1;
    dis[s]=0;
    
    while(!que.empty()){
        int num=que.front();
        que.pop();
        vis[num]=0;
        for(int i=0;i<head[num].size();i++){//对队首进行松弛操作 
            if(dis[head[num][i].v]>dis[num]+head[num][i].k){
                dis[head[num][i].v]=dis[num]+head[num][i].k;
                if(!vis[head[num][i].v]){//如果队首更新,就把与队首相邻的节点入队 
                    que.push(head[num][i].v);
                    vis[head[num][i].v]=1;
                }
            }
        }
    }
}

int main(){
    int s,x,y,z;
    node a;
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&z);
        a.v=y;a.k=z;
        head[x].push_back(a);
    }
    SPFA(s);
    for(int i=1;i<=n;i++){
        printf("%d ",dis[i]);
    }
    return 0;
}

5.Floyd-Warshall算法

Floyed-Warshall算法可以求出多源最短路径,即求出任意两点之间的最短路径。Floyd-Warshall算法的本质是动态规划。设\(dis_{k}[i][j]\)表示节点\(i\)和节点\(j\)之间的一条仅经过编号最大不超过\(k\)的节点最短路径的长度,那么显然\(dis_{0}[i][j]=w(i,j)\),因为\(k\)=0,所以路径中不能经过其他节点。

So how from \ (dis_ {k-1} [i] [j] \) launched \ (dis_ {k} [i ] [j] \) it? We need two possibilities:

  1. \ (dis_ {k} [i ] [j] \) is one without \ (K \) length of a path node, then the \ (dis_ {k} [i ] [j] = dis_ {k-1} [ I] [J] \) .
  2. \ (dis_ {k} [i ] [j] \) is a through \ (\ I) the length of path of the node, then \ (dis_ {k} [i ] [j] = dis_ {k-1} [i ] [K] +. 1-dis_ {I} [K] [J] \) (these paths divided into two parts)

Thus, we can obtain the state transition equation:
\ (dis_ {K} [I] [J] = max (K-dis_. 1 {} [I] [J], K-dis_. 1 {} [I] [K] + dis_ {k-1} [k ] [j]) \)

template:

#include<bits/stdc++.h>
using namespace std;
const int INF=1e9;
int n,dis[1001][1001];

void Floyd_Warshall(){
    for(int k=1;k<=n;k++){//循环k的值 
        for(int i=1;i<=n;i++){
            for(int j=1;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];
                }
            }
        }
    }
}

int main(){
    scanf("%d",&n);
    int x,y,z;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%d",&dis[i][j]);
            if(dis[i][j]==-1){
                dis[i][j]=INF;
            }
        }
    }//邻接矩阵存图 
    
    Floyd_Warshall();
    
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(dis[i][j]==INF)
                printf("-1 ");
            else
                printf("%d ",dis[i][j]);
        }
        printf("\n");
    }
    return 0;
}

Unfinished be more ......

Guess you like

Origin www.cnblogs.com/juruoyqr/p/11286320.html