圖論(最短路&最小生成樹)

圖論

圖的定義與概念

圖的分類

圖,根據點數和邊數可分爲三種:徹底圖,稠密圖與稀疏圖。優化

徹底圖,即\(m=n^2\)的圖\((m\)爲邊數,\(n\)爲點數\()\)。如:spa

1 1 0
1 2 1
1 3 2
2 1 1
2 2 0
2 3 3
3 1 2
3 2 3
3 3 0

這個數據是一個徹底圖。code

稠密圖,即\(m\)十分接近於\(n^2\)的圖。如:blog

1 1 0
1 2 1
1 3 2
2 1 1
2 2 0
2 3 3
3 1 2

這個數據是一個稠密圖。ci

稀疏圖,即\(m\)遠遠低於\(n^2\)的圖。如:it

1 2 1
1 3 5
2 3 7
2 4 3
3 4 5

這個數據是一個稀疏圖。io

根據方向可分爲兩種:有向圖和無向圖。table

有向圖,就是邊有方向,好比說,\(1\)~\(2\)有一條邊,而\(2\)~\(1\)沒有邊,則只能從\(1\)前往\(2\),不能從\(2\)前往\(1\),相似於現實中的單行道。class

無向圖,就是邊無方向,好比說,\(1\)~\(2\)有一條邊,而\(2\)~\(1\)沒有邊,則能夠從\(1\)前往\(2\),也能夠從\(2\)前往\(1\),相似於現實中的雙行道。nio

圖的邊權

就相似於現實中路的長度。

圖大概長這樣:

圖的存儲

鄰接矩陣

適用於\(n \le 10000\)

\(Code:\)

int dis[n][n];
int inf=99999999;
for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
        if(i!=j)dis[i][j]=inf;
cin>>u>>v>>w;
dis[u][v]=w;

鄰接矩陣,就是用矩陣來存儲圖,\(dis[i][j]\)即表明\(i\)~\(j\)的最短路徑。如一個有向圖:

1 1 0
1 2 1
1 3 2
2 1 1
2 2 0
2 3 3
3 1 2

這個數據用鄰接表存儲是這樣的:

\(dis\) \(1\) \(2\) \(3\)
\(1\) 0 1 2
\(2\) 1 0 3
\(3\) 2 inf 0

邊表

適用於全部狀況。

struct edge
{
    int u,v,w;
}e[m];
...
cin>>u>>v>>w;
e[cnt]={(edge)u,v,w};

鄰接表

適用於全部狀況。

int u[m],v[m],w[m],next[2*m],first[m];
...
cin>>u[cnt]>>v[cnt]>>w[cnt];
next[cnt]=first[u[cnt]];
first[u[cnt]]=cnt;

圖的最短路

\(dijkstra(\)堆優化\()\)

時間複雜度:\(O((m+n)\) \(log\) \(n)\)

使用鄰接表。

void dijkstra(int i)
{
    for(int j=0;j<=n;j++)
        dis[j]=inf;
    dis[i]=0;
    memset(book,false,sizeof(book));
    priority_queue<pair<int,int>>q;
    q.push(make_pair(0,i));
    while(q.size())
    {
        int x=q.top().second;
        q.pop();
        if(book[x])continue;
        book[x]=true;
        for(int k=first[x];k;k=next[k])
        {
            if(dis[v[k]]>dis[u[k]]+w[k])
            {
                dis[v[k]]=dis[u[k]]+w[k];
                q.push(make_pair(-dis[v[k]],v[k]));
            }
        }
    }
}

\(SPFA\)

時間複雜度:\(O(\)\()\)

使用鄰接表\(/\)邊表。

void spfa(int i)
{
    for(int j=0;j<=n;j++)
        dis[j]=inf;
    dis[i]=0;
    memset(book,false,sizeof(book));
    queue<int>q;
    q.push(i);
    book[i]=true;
    while(!q.empty())
    {
        int k=first[q.front()];
        while(k!=-1)
        {
            if(dis[v[k]]>dis[u[k]]+w[k])
            {
                dis[v[k]]=dis[u[k]]+w[k];
                if(book[v[k]]==false)
                {
                    q.push(v[k]);
                    book[v[k]]=true;
                }
            }
            k=next[k];
        }
        book[q.front()]=false;
        q.pop();
    }
}

\(Floyd\)

時間複雜度:\(O(n^3)\)

使用鄰接矩陣。

for(int k=1;k<=n;k++)
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);

圖的最小生成樹

圖的最小生成樹指,權值和最小的生成樹。

struct edge
{
    int u,v,w;
}e[3240010];
struct bcj
{
    int father[1810];
    void start(int n)
    {for(int i=0;i<=n;i++)father[i]=i;}
    int find(int x)
    {if(father[x]!=x)father[x]=find(father[x]);return father[x];}
    void unionn(int x,int y)
    {x=find(x);y=find(y);if(x!=y)father[y]=x;}
    bool judge(int x,int y)
    {if(find(x)==find(y))return true;return false;}
};
bool cmp(edge a,edge b)
{
    return a.w<b.w;
}
...
sort(e+1,e+1+m,cmp);
uf.start(n);
for(int i=1;i<=m;i++)
{
    if(!uf.judge(e[i].u,e[i].v))
    {
        cnt++;
        ans+=e[i].w;
        uf.unionn(e[i].u,e[i].v);
     }
     if(cnt==n-1)break;
}
相關文章
相關標籤/搜索