基礎圖論


基本概念

二元組(V, E) 稱為圖。V為頂點的集合,E為V中頂點之間的邊的集合。

自環:一條邊的兩個端點是重合的。

重邊:兩個端點之間有兩條以上的邊

簡單圖:沒有自環和重邊的圖

左圖是簡單圖,右圖中存在環和重邊。

邊和向

無向邊:邊是雙向的

有向邊:單向邊,有箭頭

無向圖:只有無向邊的圖

有向圖:只有有向邊的圖

頂點的度

無向圖中,一個頂點相連的邊數稱為該頂點的度。

有向圖中,從一個頂點出發的邊數稱為該頂點的出度;到達該頂點的邊數稱為它的入度。

權和網

在圖的邊給出相關的數,稱為權。

權可以表示一個頂點到另一個頂點的距離,耗費等。帶權圖一般稱為網。

圖的種類

完全圖:任何兩個頂點之間都有邊相連的圖

n階完全圖的任意一點v有:d(v)=n-1

非完全圖:至少有兩個頂點之間無邊相連的圖

稀疏圖:邊很少的圖

稠密圖:邊很多的圖

利用圖解決數學競賽題目好牛逼啊

例:有一個1982人組成的團體,其中任意4人中至少有一人認識其他三人,求至少有多少人認識這個團體中其他所有人。(額,有點難啊

分析:轉換為圖,人為點,認識為邊。顯然,最多是全部人互相認識,即該圖為完全圖,但該題求最小數,即求非完全圖中d(v)=1981的點的最小個數。

設u,v之間無邊,則d(u)<=1980,d(v)<=1980,再設另外兩點x,y之間也無邊,則不符合題意。所以x,y必定有邊,且其中至少有一個點與u,v有邊,則至少有1979個點與其他點都有邊。

 圖的鄰接矩陣表示法

相鄰矩陣是表示結點間相鄰關系的矩陣。

若G=(V,E)是一個具有n個結點的圖,則G的相鄰矩陣是如下定義的二維數組a,其規模為n*n

 A(i,j)= 1 表示頂點i和頂點j有邊

 A(i,j)= 0 表示頂點i和頂點j無邊

有就是,兩點為1就有連線,為0無連線

鄰接矩陣為

 

同樣,網和有向圖表示為:

                                        

 空間復雜度:O(V^2)

優點:直觀,容易理解,可以直接查看任意兩點的關系。

缺點:對於稀疏圖,會有很多空間根本沒有利用。不能處理重邊。要查詢某一個頂點的所有邊,要枚舉V次。

圖的鄰接鏈表表示法(主要應對稀疏圖)

對圖的每個頂點建立一個單鏈表(n個頂點建立n個單鏈表),第i個單鏈表中的結點包含頂點Vi的所有鄰接頂點。

空間復雜度:有向圖O(V+E)無向圖O(V+2*E)   

優點節省空間,能快速找到某個頂點所有相連的頂點,而無需訪問無關頂點。

對於大多數圖來說都是稀疏圖,所以務必掌握用鄰接鏈表來存儲圖。

鄰接鏈表

struct Edge//存儲每條邊
{
int v,w,next
}e[maxn];
int k=1,head[maxn];//每條鏈表的第一條邊
void adde(int u,int v,int w)//加邊
{
    e[k].v=v;
    e[k].w=w;
    e[k].next=head[u];
    head[u]=k++;
}

OK!

拓撲排序

啥是拓撲排序

拓撲排序是對有向無環圖(Directed Acyclic Graph簡稱DAG)求出一個頂點序列,使其滿足:對於任意邊(u,v)ϵE,u在序列的位置總在v之前。 

以上摘自百科

怎么做

  1. 從有向圖中選擇一個沒有前驅(即入度為0)的頂點並且輸出它.   
  2. 從圖中刪去該頂點,並且刪去從該頂點發出的全部有向邊,並且更新點的入度.  
  3. 重復上述兩步,直到剩余的圖中不再存在沒有前趨的頂點為止.

搞爪子?

  1.  拓撲排序在有向無環圖中才能排出有效的序列,否則能判斷該有向圖有環
  2. 如果輸入的有向圖中的點,不存在入度為0的點,則該有向圖存在回路
  3. 如果存在的入度為0的點大於一個,則該有向圖肯定不存在一個可以確定的拓撲序列但並不妨礙拓撲排序

鄰接矩陣+直接法

空間開銷大,不能判斷有環

for(i=1;i<=n;i++)//外層循環n次,in[]數組用來記錄每個點的入度    
{   
        j=1;   
        while(in[j]!=0) j++;//從第一個節點開始找到一個節點入度為0的節點  
        ans[i]=j;//存儲答案    
        in[j]=-1;//將該節點的入度更新為-1    
        for( k=1;k<=n;k++)//將所有與節點j相連的節點的入度值全部減1    
            if(vis[j][k]==1in[k]--;   
} 

 

鄰接鏈表+棧

空間小,可以判斷環

bool topsort()//拓撲排序,有拓撲序返回真,否則返回假 
{
    int p,q;
    for(int i=1;i<=n;i++)//先找到入度為0的節點入棧 
    {
        if(!d[i])s.push(i);//d[i]表示i點的入度,在加邊的時候初始化d[i]
    }
    while(!s.empty())//當棧非空進行操作 
    {
        p=s.top();//記錄棧頂節點 
        s.pop();//彈出棧頂節點 
        ans[cnt++]=p;//將彈出節點存儲到結果數組,並計數 
        for(int i=head[p];i!=-1;i=e[i].next)//清除該節點的出度 
        {
            q=e[i].v;
            if(!(--d[q]))s.push(q);//如果又發現入度為0的節點,繼續入棧 
        }
    }
    if(cnt<=n)return false;//當有節點沒入棧,則說明存在環 
    return true;
}

QAQ¤^¤……


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM