Detailed explanation of the basic algorithm of ACM's graph theory

Basic Algorithms of Graph Theory

DFS, BFS
two spanning trees prim + Kruskal
4 shortest paths Dijkstra+Floyd+Bellman-Ford+SPFA


DFS&BFS

DFS - traverse all solution
templates :

void DFS( Point P ){
        for(所有P的邻接点K){
                if(K未被访问){
                         标记K;
                         DFS(K);
                         //有时候要清空之前的标记;
                }
        }
}

dfs is generally used with pruning strategies. For details,
see : https://blog.csdn.net/u010700335/article/details/44095171

BFS - Finding the optimal solution
Using queues and layers to search for
templates

Q={起点s}; 标记s为己访问;
    while (Q非空) {
        取Q队首元素u; u出队;
        所有与u相邻且未被访问的点进入队列;
        标记u为已访问;
    }
////////////////////////////////////////////////////////////
whlie (队列不空) {
    u = 对队首元素;
    首元素出队;
    for (所有与 u 邻接点 v)      //  u 的上 下 左 右 
          // if(v 的坐标在 row, col之内 
          //     并且 v 不是墙
          //     并且 v 未被遍历) 
           v 入队
} 

two spanning trees

prim

The core idea of ​​the algorithm: From the point of view of set theory, it is to continuously reduce the points closest to the tree.

weight[n] records the shortest distance to each point (i) in this set

adjex[n] records the shortest distance from which point in this set to each point (i)

visit[n]  记录哪些点属于那个集合 1为此树中,0为未归约进来的点
[cpp] view plain copy
#include <stdio.h>  
#include <string.h>  
#define N 401  
int state[N][N];  
int maps [N][N];  


int solve(int n)  
{  
    int weight[N];  
    int adjex[N];  
    int visit[N];  
    int i,j,k;  
    int minnum,ans;  
    ans=0;  
    for(i=0;i<n;i++)  
        weight[i]=maps[0][i],adjex[i]=visit[i]=0;  
    visit[0]=1;  
    for(i=1;i<n;i++)  
    {  
        minnum=0xffffff;  
        for(j=1;j<n;j++)  
            if(minnum>weight[j] && visit[j] == 0)  
                minnum=weight[j],k=j;  
        visit[k]=1;  
        ans+=minnum;  
        for(j=1;j<n;j++)  
            if(maps[k][j]<weight[j] && visit[j]==0 )  
                weight[j]=maps[k][j],adjex[j]=k;  

    }  
    return ans;  

}  






int main()  
{  
    int i,j,k,n;  

while(  scanf("%d",&n)!=EOF)  
{  
    for(i=0;i<n;i++)  
        for(j=0;j<n;j++)  
            scanf("%d",&maps[i][j]);  

    printf("%d\n",solve(n));  
}  

    return 0;  
}  

Kruskal

Core idea: Continue to build a spanning tree with the shortest edge and avoid loops.

Use union search to quickly query whether two points of an edge are in the same set, avoiding loops

[cpp] view plain copy
/* 
课堂上,归并的时候以边中最小结点编号作为连通子图的编号 
此处使用归并集 
*/  
void kruskal (edgeset ge, int n, int e)  
// ge为权按从小到大排序的边集数组  
{   
int set[MAXE], v1, v2, i, j;  
for (i=1;i<=n;i++)  
set[i]=0;   // 给set中每个元素赋初值  
i=1; // i表示获取的生成树中的边数,初值为1  
j=1; // j表示ge中的下标,初始值为1  
while (j<n && i<=e)  
// 检查该边是否加入到生成树中  
{  
v1=seeks(set,ge[i].bv);  
v2=seeks(set,ge[i].tv);  
if (v1!=v2) // 当v1,v2不在同一集合,该边加入生成树  
{  
printf(“(%d,%d)”,ge[i].bv,ge[i].tv);  
set[v1]=v2;  
j++;//j是为了判断是否已经使n个点加入生成树中,是则结束  
}  
i++;  
}  
}  
int seeks( int *set,int i)  
{  
    while(set[i]!=i)  
        i=set[i];  
    return i;  
}  

Note: The union query set can use path compression, and recursively return each time you query, so that the query time is O(1).


four shortest paths

Dijkstra

Update all point paths with the nodes in the graph each time to achieve the goal of the shortest path
1. Loop n times
2. Find the minimum point x in all d[] nodes
3. Mark the point x
4. For the edges starting from this point update d[y]=min{d[y],d[x]+w[x][y]}

[cpp] view plain copy
void Dijkstra(){       
       int k;  
    for(int i=1;i<=n;i++)        
        dis[i] = map[1][i];          
    for(int i=1;i<n;i++){        
        int tmin = maxint;       
        for(int j=1;j<=n;j++)         
            if( !used[j] && tmin > dis[j] ){          
                tmin = dis[j];  
                k = j;  
            }  
            used[k] = 1;        
        for(int j=1;j<=n;j++)         
            if( !used[j] &&dis[k] + map[k][j] < dis[j] )  //跟佛洛依德算法相似,看中间是否有个中间点  
                dis[j] = dis[k] + map[k][j];       
     }       
     printf("%d",dis[n]);  
} /* 求1到N的最短路,dis[i] 表示第i个点到第一个点的最短路 By Ping*/  
//找未用过的最短邻接点,以此为中转修正其余点,直到全部完成  

Floyd

Note the use of the updated intermediate node k at the outermost level. (I wrote one in the innermost layer before, which violated the method. Update all paths with one node)

Can be computed on graphs with negative weighted edges

For any two points (i,j), all paths are updated with one node at a time. At the same time, we know that the shortest distance between any two points passes through at most n-1 nodes

In this way, the order of added points for a particular two points becomes unimportant

1. The added points are no longer required on the way to two points, it doesn't matter

2. From being directly connected to two points to being indirectly connected, in line with our thinking

3. Most importantly, if it is out of order, what if it starts from the middle one?

If it is an intermediate point, the shortest path between the points connected to it will be updated, which is helpful for the next intermediate point, and it is also equivalent to repairing the "middle road" first.

[cpp] view plain copy
//d[i][j]用于记录从i到j的最短路径的长度  
// path[i][j]用于记录从i到j的最短路径上j点的前一个节点  
//d[i][j]用于记录从i到j的最短路径的长度  
void Floyd(int num,int **path,int**d,int **a)  


{  
    int i,j,k;  
    for(i=0;i<num;i++)  
    {  
        for(j=0;j<num;j++)//初始化  
        {  
            if(a[i][j]<max) path[i][j]=j;//从i到j有路径  
            else path[i][j]=-1;  
            d[i][j]=a[i][j];  
        }  
    }  
    for(k=0;k<num;k++)  
        for(i=0;i<num;i++)  
            for(j=0;j<num;j++)  
                if(d[i][j]>d[i][k]+d[k][j])//从i到j的一条更短的路径  
                {  
                    d[i][j]=d[i][k]+d[k][j];  
                    path[i][j]=path[i][k];  
                }  
}  

Bellman-Ford

The previous shortest path requires no negative edges in the graph, this algorithm can find the shortest path in the graph with negative edges.

Relax each edge, and each relaxation can add at least one arrival point, so the outer loop is at most v-1 times,

The difference with Dijkstra is that Dijkstra does not use a point each time it is used, whereas each edge of Bellman is used each time.

Dijkstra always finds a path from the set of shortest paths found to the set that has not been found, and does not recalculate the shortest path of points inside the set.

explain:

Since dijkstra is greedy, it finds a point (dmin) closest to the source point each time, and then sets the distance as the shortest path from this point to the source point (d[i]<–dmin); but if there is a negative weight edge, then it is possible to first pass a secondary advantage (dmin') that is not the closest to the source point, and then pass this negative weight edge L (L<0), making the sum of the paths smaller (dmin'+L

算法实现:

[cpp] view plain copy
#include <iostream>  
using namespace std;  
const int maxnum = 100;  
const int maxint = 99999;  

// 边,  
typedef struct Edge{  
    int u, v;    // 起点,重点  
    int weight;  // 边的权值  
}Edge;  

Edge edge[maxnum];     // 保存边的值  
int  dist[maxnum];     // 结点到源点最小距离  

int nodenum, edgenum, source;    // 结点数,边数,源点  

// 初始化图  
void init()  
{  
    // 输入结点数,边数,源点  
    cin >> nodenum >> edgenum >> source;  
    for(int i=1; i<=nodenum; ++i)  
        dist[i] = maxint;  
    dist[source] = 0;  
    for(int i=1; i<=edgenum; ++i)  
    {  
        cin >> edge[i].u >> edge[i].v >> edge[i].weight;  
        if(edge[i].u == source)          //注意这里设置初始情况  
            dist[edge[i].v] = edge[i].weight;  
    }  
}  

// 松弛计算  
void relax(int u, int v, int weight)  
{  
    if(dist[v] > dist[u] + weight)  
        dist[v] = dist[u] + weight;  
}  

bool Bellman_Ford()  
{  
    for(int i=1; i<=nodenum-1; ++i)  
        for(int j=1; j<=edgenum; ++j)  
            relax(edge[j].u, edge[j].v, edge[j].weight);  
    bool flag = 1;  
    // 判断是否有负环路  
    for(int i=1; i<=edgenum; ++i)  
        if(dist[edge[i].v] > dist[edge[i].u] + edge[i].weight)  
        {  
            flag = 0;  
            break;  
        }  
    return flag;  
}  
int main()  
{  
    //freopen("input3.txt", "r", stdin);  
    init();  
    if(Bellman_Ford())  
        for(int i = 1 ;i <= nodenum; i++)  
            cout << dist[i] << endl;  
    return 0;  
}  

SPFA (Universal Shortest Path Algorithm)

For a detailed explanation: https://blog.csdn.net/xunalove/article/details/70045815
When initializing, only add out edges, not add in edges or you will resist

The principle of optimizing the Bellman-Ford algorithm using queues is to use:

It is not necessary to relax all the edges every time, only the edges connected to the last updated point will have an effect on the next relaxation. And SPFA records these just updated points

If a point enters the team N times, it is judged that the graph has a cycle and exits. or empty, exit

Only the starting point is enqueued during initialization.

void  spfa(s);  //求单源点s到其它各顶点的最短距离
    for i=1 to n do { dis[i]=∞; vis[i]=false; }   //初始化每点到s的距离,不在队列
    dis[s]=0;  //将dis[源点]设为0
    vis[s]=true; //源点s入队列
    head=0; tail=1; q[tail]=s; //源点s入队, 头尾指针赋初值
    while head<tail do {
       head+1;  //队首出队
       v=q[head];  //队首结点v
       vis[v]=false;  //释放对v的标记,可以重新入队
       for 每条边(v,i)  //对于与队首v相连的每一条边
      if (dis[i]>dis[v]+a[v][i])  //如果不满足三角形性质
        dis[i] = dis[v] + a[v][i]   //松弛dis[i]
        if (vis[i]=false) {tail+1; q[tail]=i; vis[i]=true;} //不在队列,则加入队列
    }

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325817203&siteId=291194637