グラフ理論研究ノート

グラフ理論研究ノート

グラフ理論における最近この口コミこんにゃく、忘れるために彼自身の基本的なグラフ理論を発見したので、私はレビューのために、このような研究ノートを書くつもり。

、図3に示します。

1.隣接行列

2次元メモリアレイの側で、\(マップ[I] [J] = Kは\)ポイントを表し\(Iは\)ポイントを\(J \)重み値\(K \) ティム側は、O(1)の複雑さの両面を確認し、あまりにも多くのスペースを取ります。

テンプレート:

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.隣接リスト

\(N- \)送信されたストレージノード、小さいフットプリントの各アレイの両側。

テンプレート(ベクター法):

#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;
}

第二に、トポロジカル整列

このブログを参照してください。

第三に、最小スパニングツリー

1.定義

最小スパニングツリー通信無向グラフは図の部分グラフであり、図の頂点の全てを含む、木であり、右側が最小です。

2.Primアルゴリズム

点のセットを提供\(Q \)は、エッジを探し、(任意のノードであってもよい)のみ初期ノードを含む\((U、V)\) を満たす点\(U \)コレクション内の\(Q \) ポイント\(V \)セットではない\(Q \) 右側はそのようなすべてのエッジの最小であり、このエッジは、最小スパニングツリーのエッジである、ポイント\(V \)は、のセットをも添加します\(Q \)セットされるまで、このような操作では、繰り返される\(Q \)は、すべてのノードを含みます。そして、すべての辺のセットは、この図の最小全域木を見つけることです。

比喩的に言えば、つまり、マップ上のポイントを選択し、この点の周りに円を描きます。(AS)

ここでは、画像の説明を入力します。

側面から選択し、次に最小量(即ち、図中のエッジ5と6の間)と交差する円の内側、次にエッジノードの周囲の最小重量も円に向けられています。(AS)

ここでは、画像の説明を入力します。

次いで、検索円最小エッジの交差点の右側に、ノードは、円の内側の円を指摘し、そしてように円がすべてのノードを含むまで、すべてのソート側は最小全域木であります。

テンプレート:

#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アルゴリズム

最初の焦点の異なる点にすべてのノードの各点においてのみつのノードに焦点を当てます。次に、エッジを見つけるために横断される\((U、V)\)満たす、ノード\(U \)とノード\(V \)の同じセット内にない、と最小満足エッジ重みこの条件です。次いで、このエッジは、最小スパニングツリーのエッジです。ノード\(U \)とノード\(V \)合併、すべての点まで、全ての側のコレクションをコレクションをエッジを見つけ、マージに進む前に、同じセット内にある集合、その後見つけ、これをあります図の最小スパニングツリー。

クラスカル法は、達成するためにばらばらセットを利用することができます。

比喩的に言えば、つまり、全ての周囲のノードのビューに(図示のように)円を描きます。

ここでは、画像の説明を入力します。

次いで、エッジ(すなわちリングは、2つの辺の交点を有する)最小エッジ重み値(即ち、図5-6のエッジ)、これら2個の合成環を見つける環に含まれていません。(AS)

ここでは、画像の説明を入力します。

サークル内のすべてのノードまでこの操作を繰り返し、その後、すべてのソート側は最小全域木です。

テンプレート:

#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差アルゴリズムとクラスカル法

アルゴリズム プリム クラスカル
時間複雑 \(O(Vの*ログ(V))\) \((E *ログ(E))\)
スコープ 密なグラフ スパースグラフ

注:\(V \)ドットで、\(E \)は、エッジの数です。
達成するために、ヒープの複雑さを最適化することにより、プリムのアルゴリズムのニーズの後に\(O(V *をLOG(V))\)

第四に、最短

1.最短経路問題

最短路问题(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,所以路径中不能经过其他节点。

それでは、どのから\(dis_ {K-1} [I] [j]が\) 起動\(dis_ {K} [I ] [j]を\) 、それ?我々は2つの可能性を必要とします:

  1. \(dis_ {K} [I ] [j]が\) のないものである\(K \)、パスノードの長さ、\(dis_ {K} [I ] [J] = dis_ {K-1} [ I] [J] \)
  2. \(dis_ {K} [I ] [j]が\) によるものである\(\ I)、ノードの経路の長さ、\(dis_ {K} [I ] [J] = dis_ {K-1} [I ] [K] +。1-dis_ {I} [K] [J] \) これらのパスは、2つの部分に分割)

したがって、我々は状態遷移方程式を得ることができる
。\(dis_ {K} [I] [J] = MAX(K-dis_ 1 {} [I]、[J]、K-dis_ 1 {} [I] [K] + dis_ {K-1} [K ] [J])\)

テンプレート:

#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;
}

未完のはよりも......

おすすめ

転載: www.cnblogs.com/juruoyqr/p/11286320.html