1. 程式人生 > >圖論模板

圖論模板

col 實現 lan 沒事 建立 sizeof ont scanf put

今天閑的沒事來整理一下圖論的模板(某些出自他處)

1、sp的鄰接矩陣(adjacency matrix

定義:

  邏輯結構分為兩部分:V和E集合。因此,用一個一維數組V(vertex)存放圖中所有頂點數據;用一個二維數組E(edge)存放頂點間關系(邊或弧)的數據,這個二維數組稱為鄰接矩陣。

代碼實現:

//無向圖
#include<iostream>
#include<cstring>
using namespace std;

int e,n;
int g[101][101];
int w;

int main() {
    int
x,y,z; cin>>n>>e; memset(g,0,sizeof(g)); for(int i=1; i<=e; i++) { cin>>x>>y>>z; g[x][y]=g[y][x]=z; } for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) { printf("%d ",g[i][j]); } cout
<<endl; } }

2、sp的鄰接表(Adjacency list

定義:

  鄰接表,存儲方法跟樹的孩子鏈表示法相類似,是一種順序分配和鏈式分配相結合的存儲結構。如這個表頭結點所對應的頂點存在相鄰頂點,則把相鄰頂點依次存放於表頭結點所指向的單向鏈表中。

  對於無向圖來說,使用鄰接表進行存儲也會出現數據冗余,表頭結點A所指鏈表中存在一個指向C的表結點的同時,表頭結點C所指鏈表也會存在一個指向A的表結點。

  鄰接表是圖的一種最主要存儲結構,用來描述圖上的每一個點。對圖的每個頂點建立一個容器(n個頂點建立n個容器),第i個容器中的結點包含頂點Vi的所有鄰接頂點。實際上我們常用的鄰接矩陣就是一種未離散化每個點的邊集的鄰接表。

  在有向圖中,描述每個點向別的節點連的邊(點a->點b這種情況)。

  在無向圖中,描述每個點所有的邊(點a-點b這種情況)

代碼實現:

#include<bits/stdc++.h>
using namespace std;

struct edge {
    int u,v,w,next;
} edge[10001];

int first[1001],num=0;
int n,m;

void add(int u,int v,int w) {
    num++;
    edge[num].next=first[u];
    edge[num].u=u;
    edge[num].w=w;
    edge[num].v=v;
    first[u]=num;
}


int main() {
    scanf("%d%d",&n,&m);
    int u,v,w;
    for(int i=1; i<=m; i++) {
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
    }
    for(int i=1; i<=n; i++) {
        int k=first[i];
        while(k!=0) {
            cout<<edge[k].u<<" "<<edge[k].v<<endl;
            k=edge[k].next;
        }
    }
    return 0;
}

3、sp的深搜遍歷(DFS

#include<bits/stdc++.h>
#include<cstring>
using namespace std;

int e[1010][1010];
int vis[1010];
int n,m;

void dfs(int u);

int main() {
    cin>>n>>m;
    char x,y;
    for(int i=1; i<=m; i++) {
        cin>>x>>y;
        e[x-65][y-65]=e[y-65][x-65]=1;
    }
    dfs(0);
    return 0;
}

void dfs(int u) {
    cout<<char(u+65)<<" ";
    vis[u]=1;
    for(int i=1; i<=n; i++) {
        if(e[u][i]==1 && vis[i]==0) {
            dfs(i);
        }
    }
}

4、sp的廣搜遍歷(BFS

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

int e[1010][1010];
int vis[1010],que[1010];
int n,m;

void bfs(int u);

int main() {
    cin>>n>>m;
    char x,y;
    for(int i=1; i<=m; i++) {
        cin>>x>>y;
        e[x-65][y-65]=e[y-65][x-65]=1;
    }
    bfs(0);
    return 0;
}

void bfs(int u) {
    int h=0,t=1;
    que[t]=u;
    cout<<char(u+65)<<" ";
    vis[u]=1;
    while(h<t) {
        h++;
        int k=que[h];
        for(int i=1; i<=n; i++) {
            if(e[k][i]==1 && vis[i]==0) {
                t++;
                cout<<char(i+65)<<" ";
                vis[i]=1;
                que[t]=i;
            }
        }
    }
}

5、spfloyed算法

原理:

  利用鄰接矩陣判斷

步驟:

  1.把所有的邊賦值無窮大,能通過的賦值

  2. 三個for循環,k,i,j, k表示從i->j可以走i->k->j,不斷取小值

  3.選取出發點和結束點,直接輸出就是最短路

代碼實現:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int  MAX = 300;
const int inf = 0x3f3f3f3f;
int d[MAX],ditu[MAX][MAX];
int main() {
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n ; i++)
        for(int j = 1; j <= n; j++)
            ditu[i][j] = inf;
    int x, y, t;
    for(int i = 1; i <=m; i++) {
        scanf("%d%d%d",&x,&y,&t);
        ditu[x][y] = t;
    }
    for(int k = 1; k <= n ; k++) {
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                if(i!=k && i!=j && ditu[i][j]>ditu[i][k]+ditu[k][j])
                    ditu[i][j] = ditu[i][k]+ditu[k][j];
            }
        }
    }
    printf("%d",ditu[1][n]);
    return 0;
}

然後我們可以進行兩個優化

(1)、剪枝:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
int qread() {
    int x=0;
    char ch=getchar();
    while(ch<0||ch>9)ch=getchar();
    while(ch>=0&&ch<=9) {
        x=x*10+ch-0;
        ch=getchar();
    }
    return x;
}
template<typename T>inline void write(T x) {
    if(x<0)putchar(-),x*=-1;
    if(x>=10)write(x/10);
    putchar(x%10+0);
}
int dis[1010][1010];
int main() {
    int n,m,a,b,s;
    cin>>n>>m;
    for(int i=1; i<=m; ++i) {
        a=qread();
        b=qread();
        s=qread();
        dis[a][b]=dis[b][a]=s;
    }
    for(int k=1; k<=n; ++k)
        for(int i=1; i<=n; ++i)
            if(i!=k) {
                for(int j=1; j<=i; ++j) {
                    if(i!=j&&j!=k&&dis[i][j]>dis[i][k]*dis[k][j]&&dis[i][k]!=0&&dis[k][j]!=0) {
                        dis[i][j]=dis[i][k]*dis[k][j];
                        dis[i][j]%=9987;
                        dis[j][i]=dis[i][j];
                    }
                }
            }
    write(dis[1][n]);
    return 0;
}

(2)、判斷

#include<bits/stdc++.h>
using namespace std;
long long g[1010][1010];
long long n,m,a,b,s;

int qread()
{
    int x=0,f=1;
    char c=getchar();
    while(c<0||c>9){if(c==-)f=-1;c=getchar();}
    while(c>=0&&c<=9){x=x*10+c-0;c=getchar();}
    return x*f;
}

int main() {
    n=qread();m=qread();
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=n; j++) {
            g[i][j]=0xffffff;
        }
    }
    for(int i=1; i<=m; i++) {
        a=qread();b=qread();s=qread();
        g[a][b]=s;
    }
    for(int k=1; k<=n; k++)
        for(int i=1; i<=n; i++)
            if(i!=k && g[i][k]!=0xffffff) {
                for(int j=1; j<=n; j++) {
                    if(i!=j&&j!=k&&g[i][j]>g[i][k]*g[k][j]) {
                        g[i][j]=g[i][k]*g[k][j];
                    }
                }
            }
    printf("%lld",g[1][n]%9987);
    return 0;
}

插一句,一起食用更佳哦

6、spDijkstra算法

  Dijkstra(迪傑斯特拉)算法是典型的單源最短路徑算法,用於計算一個節點到其他所有節點的最短路徑。主要特點是以起始點為中心向外層層擴展,直到擴展到終點為止。Dijkstra算法是很有代表性的最短路徑算法,在很多專業課程中都作為基本內容有詳細的介紹,如數據結構,圖論,運籌學等等。Dijkstra一般的表述通常有兩種方式,一種用永久和臨時標號方式,一種是用OPEN, CLOSE表的方式,這裏均采用永久和臨時標號的方式。註意該算法要求圖中不存在負權邊。

代碼實現:

//Dijkstra
#include<iostream>
#include<cstdio>
#define INF 9999999
using namespace std;


const int maxn=1010;
int dis[maxn];//儲存單源最短路徑 
int w[maxn][maxn];//初始儲存兩點之間是否有路徑 
int f[maxn]= {0};
int n,m,s;
int x,y,z;

//輸入及初步處理 
void work() {
    cin>>n>>m>>s;
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=n; j++) {
            if(j==i)w[i][j]=0;//自己到自己的路徑是0 
            else w[i][j]=INF;//否則初始化為最大值 
        }
    }
    for(int i=1; i<=m; i++) {
        scanf("%d%d%d",&x,&y,&z);//分別代表x到y之間有邊,權值為z 
        w[x][y]=z;//無向圖的話 
        w[y][x]=z;
    }
    for(int i=1; i<=n; i++) {
        dis[i]=w[s][i];//有路徑唄 
        f[i]=0;
    }
}

//迪傑斯特拉算法求單源最短路徑
//這就是模板啦!! 
//木啥好說的 
void dijkstra() {
    dis[s]=0;
    f[s]=1;
    for(int i=1; i<=n; i++) {
        int mind=INF;
        int k;
        for(int j=1; j<=n; j++) {
            if(dis[j]<mind && !f[j]) {
                mind=dis[j];
                k=j;
            }
        }
        if(mind==INF)break;//這裏我經常忘記恒等於號,錯了好幾遍
        f[k]=1;
        for(int j=1; j<=n; j++) {
            if(dis[j]>dis[k]+w[k][j]) {
                dis[j]=dis[k]+w[k][j];
            }
        }
    }
}

//輸出函數
void write() { 
    for(int i=1; i<=n; i++) {
        cout<<dis[i]<<" ";
    }
}

int main() {
    work();
    dijkstra();
    write();
    return 0;
}

可以進行堆優化

#include<algorithm>
#include<iostream>
#include<cstring>
#include<queue> 
using namespace std;
typedef pair<int,int> pairs;
priority_queue<pairs,vector<pairs>,greater<pairs> >q ;
int dis[100007],head[100007];
bool vis[100007];
int n,m,s,t,cnt;
inline int read(){
    int x=0,f=0;char ch=getchar();
    while(!isdigit(ch))f|=ch==-,ch=getchar();
    while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
    return f?-x:x;
}
struct Edge{
    int next,to,w;
}edge[100007];
void add_edge(int from,int to,int w){
    edge[++cnt].next=head[from];edge[cnt].w=w;
    edge[cnt].to=to;head[from]=cnt;
}
inline void dijkstra(){
    memset(dis,0x3f,sizeof dis);
    dis[s]=0;
    q.push(make_pair(dis[s],s));
    while(!q.empty()){
        int x=q.top().second;q.pop();
        if(vis[x]) continue;
        vis[x]=1;
        for(int i=head[x];i;i=edge[i].next){
            int to=edge[i].to;
            if(dis[x]+edge[i].w<dis[to]){
                dis[to]=dis[x]+edge[i].w;
                q.push(make_pair(dis[to],to));
            }
        }
    }
}

int main(){
    n=read(),m=read(),s=read();
    for(int i=1;i<=m;++i){
        int u=read(),v=read(),w=read();
        add_edge(u,v,w);
    }
    dijkstra();
    for(int i=1;i<=n;++i)printf("%d ",dis[i]);
    return 0;
}

圖論模板