QBXT Day 5圖論相關

圖論是NOIP的一個非常重要的考點,換句話說,沒有圖論,NOIP的考綱就得少一大半(雖然很NOIP沒有考綱)

圖論這玩意吧,和數論一樣是非常變態的東西,知識點又多又雜,但是好在一個事,他比較直觀比較好想

對于一張圖而言,我們定義圖是一種由邊和點構成的的一個玩意(其實是嚴謹定義我記不住了QWQ,但是不影響學習)

一般來說,圖的存儲難度主要在記錄邊的信息
無向圖的存儲中,只需要將一條無向邊拆成兩條即可
鄰接矩陣:用一個二維數組 edg[N][N] 表示
edg[i][j] 就對應由 i 到 j 的邊信息
edg[i][j] 可以記錄 Bool,也可以記錄邊權
缺點:如果有重邊有時候不好處理
空間復雜度 O(V^2)

點度等額外信息也是很好維護的

#include <bits/stdc++.h>using namespace std;const int N = 5005;int ideg[N], odeg[N], n, m, edg[N][N];
bool visited[N];void travel(int u, int distance)
{cout << u << " " << distance << endl; visited[u] = true;for (int v = 1; v <= n; v++)if (edg[u][v] != -1 && !visited[v])//是否已經訪問過 travel(v, distance + edg[u][v]); //if there is an edge (u, v) and v has not been visited, then travel(v)
}
int main()
{cin >> n >> m;memset(edg, -1, sizeof edg);memset(visited, false, sizeof visited);for (int u, v, w, i = 1; i <= m; i++)cin >> u >> v >> w, edg[u][v] = w, odeg[u]++, ideg[v]++;//出度和入度 for (int i = 1; i <= n; i++)cout << ideg[i] << " " << odeg[i] << endl;for (int i = 1; i <= n; i++)if (!visited[i]) travel(i, 0);
}/*
Given a graph with N nodes and M unidirectional edges.
Each edge e_i starts from u_i to v_i and weights w_i
Output a travelsal from node 1 and output degree of each node.
*/

其實這個英文注釋也還蠻不錯的啊

鄰接矩陣本質上其實就是一個二維數組,它在存儲一個稠密圖的時候效率比較好,但是稀松圖的話就非常浪費空間

所以我們就沒有必要用二維數組記錄信息,我們只需要對每一個點記錄他的出邊就行

這樣記的話,復雜度就是他的邊數

對每一個點 u 記錄一個 List[u],包含所有從 u 出發的邊
直接用數組實現 List[u]?讀入邊之前不知道 List[u] 長度
手寫鏈表(鏈式前向星)
用 STL 中的 vector 實現變長數組,當然你想要手寫指針也沒問題
只需要 O(V + E) 的空間就能實現圖的存儲(邊數加點數)

其實寫這個鏈表存儲0有很多方式啊,你可以用指針,手寫指針,也可以用vector ,還可以用數組毛模擬

我們詳細理解一下代碼

#include <bits/stdc++.h>using namespace std;const int N = 5005;struct edge {int u, v, w; edge *next;//next指針指向 edge(int _u, int _v, int _w, edge *_next):u(_u), v(_v), w(_w), next(_next) {}
};
edge *head[N]; //List[u] 最前面的節點是誰 
int ideg[N], odeg[N], n, m;
bool visited[N];void add(int u, int v, int w)
{edge *e = new edge(u, v, w, head[u]);head[u] = e;
}
void travel(int u, int distance)
{cout << u << " " << distance << endl; visited[u] = true;for (edge *e = head[u]; e ; e = e -> next)if (!visited[e -> v])travel(e -> v, distance + e -> w); //if there is an edge (u, v) and v has not been visited, then travel(v)
}
int main()
{cin >> n >> m;memset(visited, false, sizeof visited);memset(head, 0, sizeof head);for (int u, v, w, i = 1; i <= m; i++)cin >> u >> v >> w, add(u, v, w), odeg[u]++, ideg[v]++;for (int i = 1; i <= n; i++)cout << ideg[i] << " " << odeg[i] << endl;for (int i = 1; i <= n; i++)if (!visited[i]) travel(i, 0);
}/*
Given a graph with N nodes and M unidirectional edges.
Each edge e_i starts from u_i to v_i and weights w_i
Output a travelsal from node 1 and output degree of each node.
*/

但是我個人是不用指針的,因為可能還是不習慣的原因吧,而且指針的寫法并沒有什么特別的優點

還有一個數組模擬版本

#include <bits/stdc++.h>using namespace std;const int N = 5005;struct edge {int u, v, w, next;
}edg[N];
int head[N]; //List[u] stores all edges start from u
int ideg[N], odeg[N], n, m, cnt; //cnt: numbers of edges
bool visited[N];void add(int u, int v, int w)
{int e = ++cnt;edg[e] = (edge){u, v, w, head[u]};head[u] = e;
}
void travel(int u, int distance)
{cout << u << " " << distance << endl; visited[u] = true;for (int e = head[u]; e ; e = edg[e].next)if (!visited[edg[e].v])travel(edg[e].v, distance + edg[e].w); //if there is an edge (u, v) and v has not been visited, then travel(v)
}
int main()
{cin >> n >> m; cnt = 0;memset(visited, false, sizeof visited);memset(head, 0, sizeof head);for (int u, v, w, i = 1; i <= m; i++)cin >> u >> v >> w, add(u, v, w), odeg[u]++, ideg[v]++;for (int i = 1; i <= n; i++)cout << ideg[i] << " " << odeg[i] << endl;for (int i = 1; i <= n; i++)if (!visited[i]) travel(i, 0);
}/*
Given a graph with N nodes and M unidirectional edges.
Each edge e_i starts from u_i to v_i and weights w_i
Output a travelsal from node 1 and output degree of each node.
*/

但是數組模擬必然是逃不開浪費時間過多的,這個事就很討厭了,鄰接矩陣以其優秀的可讀性以及構造性換來了不少空間,唉

我個人現在是這樣的,判斷變數和點數的值,如果差別較大,那么出題人可能是想構造菊花樹之類的,差別較小就意味著稠密,那么寫鄰接矩陣更節省時間(前提是你兩個都能用)

還有一種寫法是用vector

拋去鄰接矩陣不講,如果我們用edg[u][i]表示從u出發的第i條邊,這樣實際上還是O(n^2)的,所以我們要用一個能夠自己改變長度的STL,這樣能讓空間最大化

#include <bits/stdc++.h>using namespace std;const int N = 5005;struct edge {int u, v, w;
};
vector<edge> edg[N]; //edge記錄變長數組記錄的是什么類型 
int ideg[N], odeg[N], n, m, cnt; //cnt: numbers of edges
bool visited[N];void add(int u, int v, int w)
{edg[u].push_back((edge){u, v, w});//一個強制類型轉換 
}
void travel(int u, int distance)
{cout << u << " " << distance << endl; visited[u] = true;for (int e = 0; e < edg[u].size(); e++)//遍歷邊 if (!visited[edg[u][e].v])//以u出發的第e條出邊 travel(edg[u][e].v, distance + edg[u][e].w); //if there is an edge (u, v) and v has not been visited, then travel(v)
}
int main()
{cin >> n >> m; cnt = 0;memset(visited, false, sizeof visited);for (int u, v, w, i = 1; i <= m; i++)cin >> u >> v >> w, add(u, v, w), odeg[u]++, ideg[v]++;for (int i = 1; i <= n; i++)cout << ideg[i] << " " << odeg[i] << endl;for (int i = 1; i <= n; i++)if (!visited[i]) travel(i, 0);
}/*
Given a graph with N nodes and M unidirectional edges.
Each edge e_i starts from u_i to v_i and weights w_i
Output a travelsal from node 1 and output degree of each node.
*/

要注意的是,c++的STL數組默認都是以0為結尾的、

vector是這樣構造的

<>里面寫的是變量類型,可以是int 或者float或者結構體

生成樹

我們考慮一個聯通的無向圖,我們考慮找出這個圖當中的子圖(點的數量是一樣的,可以刪掉邊)

給定一個連通無向圖 G = (V; E)
E′ ? E
G′ = (V; E′) 構成一棵樹
G′ 就是 G 的一個生成樹

而且我們可以發現生成樹不是唯一的,而且我們可以知道的是生成樹的數量是指數級別的

那么最小生成樹其實就是生成樹當中最大邊權的值最小

?怎么求呢?

Algorithms for Minimal Spanning Tree:
Kruskal
Prim
Kosaraju

Kruskal

克魯斯卡爾的思想是貪心加上并查集

我們只把所有的邊的信息存下來,而不用存圖(因為最小生成樹只問你最小邊權和之類的問題,而不文)

,對于所有的邊權進行排序,找到當前邊權最小的邊 e : (u; v)
如果 u 和 v 已經連通,則直接刪除這條邊(這里的判斷就是用并查集的思想,如果最終并查集的指向指到了一個同一個點,那么就是聯通的啊)
如果 u 和 v 已經未連通,將之加入生成樹
重復上述過程

這個稱為Rigorous proof(消圈算法)

Kruskal的證明方法很迷啊,就感性理解一下就好

畢竟貪心這東西證明正確性還是挺困難的。

Prim的做法是,我們找一個連通塊,我們把和這個連通塊最短的點加到連通塊當中去(這倆都可以用堆優化)

Kosaraju的做法是,我們有很多連通塊,然后第一輪的時候對于每一個連通塊找到和它相連的最短的邊,就把這兩個集合連接起來

?

轉載于:https://www.cnblogs.com/this-is-M/p/10806609.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/386887.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/386887.shtml
英文地址,請注明出處:http://en.pswp.cn/news/386887.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

RUNOOB python練習題47 交換兩個變量值

用來練手的python練習題&#xff0c;原題鏈接: python練習實例47 題干: 兩個變量值互換 在C語言C中我們要構造一個能交換兩個變量值的函數很方便&#xff0c;我們可以使用指針&#xff0c;或者C中的引用。那么在沒有指針的python中如何構造一個可以交換兩個變量值的函數呢&am…

tensorflow一元二次函數擬合

先看下要做的內容&#xff0c;創建一元二次函數yx平方-0.5&#xff0c;其中為了更符合散點圖模擬需要&#xff0c;在方程加噪點&#xff0c;以標準方差0.05行駛&#xff0c;如圖所示 折線圖 散點圖 下面我們要做的&#xff0c;是要計算機自動擬合出該散點圖的函數&#xff0…

hibernate緩存機制與N+1問題

在項目中遇到的趣事 本文基于hibernate緩存機制與N1問題展開思考&#xff0c; 先介紹何為N1問題 再hibernate中用list()獲得對象&#xff1a; 1 /**2 * 此時會發出一條sql&#xff0c;將30個學生全部查詢出來3 */4 List<Student> …

lambda函數 RUNOOB python練習題49

用來練手的python練習題&#xff0c;原題鏈接python練習實例49 該練習題主要是關于lambda函數的使用方法&#xff0c;本文就python中的lambda函數做出一點總結。 1. lambda函數的定義與調用 在python中&#xff0c;我們都知道使用def關鍵詞來定義一個函數, 例如一個最簡單的…

kubernetes(k8s)安裝部署

Kubernetes是一個開源的&#xff0c;用于管理云平臺中多個主機上的容器化的應用&#xff0c;Kubernetes的目標是讓部署容器化的應用簡單并且高效,Kubernetes提供了應用部署&#xff0c;規劃&#xff0c;更新&#xff0c;維護的一種機制。 Kubernetes一個核心的特點就是能夠自主…

react typescript 子組件調用父組件

//父組件 import * as React from reactimport { Input } from antdconst Search Input.Searchimport "./index.less"import Child from "./compon/list" interface IProps { MakeMoney?: () > void //暴露方法} export default class ProjectLis…

python random隨機數 RUNOOB python練習題50

用來練手的python練習題&#xff0c;原題鏈接: python練習實例50、 該練習題主要包含了random模塊隨機數的應用&#xff0c;下面給出幾個常用的模塊內函數。 1. 生成浮點型隨機小數 最簡單的&#xff0c;就是用random函數&#xff0c;生成 [0.0,1.0)[0.0, 1.0)[0.0,1.0)范圍…

Spring Cloud Eureka Consul使用和對比

Spring Cloud簡介 最大的區別是Eureka保證AP, Consul為CP。 Consul強一致性(C)帶來的是&#xff1a; 服務注冊相比Eureka會稍慢一些。因為Consul的raft協議要求必須過半數的節點都寫入成功才認為注冊成功 Leader掛掉時&#xff0c;重新選舉期間整個consul不可用。保證了強一致…

符號 RUNOOB python練習題 51

用來練手的python練習題&#xff0c;原題鏈接: python練習實例51 python中的 & 和 | 使用過程中&#xff0c;變量類型不同&#xff0c;這兩個符號的作用也不同。 1. 對于數字變量&#xff0c;&\&& 和 ∣|∣ 用于逐位運算 # 二進制逐位邏輯與門運算 a 0b110…

Eclipse里的快捷鍵

MyEclipse 快捷鍵1(CTRL) ------------------------------------- Ctrl1 快速修復 CtrlD: 刪除當前行 CtrlQ 定位到最后編輯的地方 CtrlL 定位在某行 CtrlO 快速顯示 OutLine CtrlT 快速顯示當前類的繼承結構 CtrlW 關閉當前Editer CtrlK 快速定位到下一個 CtrlE 快…

Python打印楊輝三角形 RUNOOB python練習題61

用來練手的python練習題&#xff0c;原題鏈接: python練習實例61 題干: 打印出楊輝三角形 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 1 6 15 20 15 6 1 1 7 21 35 35 21 7 1 1 8 28 56 70 56 28 8 1 1 9 36 84 126 126 84 36 9 1 實現代碼如下: import numpy as nptable…

使用Docker快速搭建Tensorflow開發環境

當我剛開始學習使用scikit-learn時&#xff0c;總是會出現各種各樣的包依賴問題&#xff0c;兜兜轉轉了一遍才全部安裝好&#xff0c;現在的機器學習算法開發者大都使用tensorflow、pytorch來實現自己的想法&#xff0c;但依然會面臨各種包版本和依賴的問題&#xff0c;有一段時…

Java服務GC參數調優案例

這段時間在整理jvm系列的文章&#xff0c;無意中發現本文&#xff0c;作者思路清晰通過步步分析最終解決問題。我個人特別喜歡這種實戰類的內容&#xff0c;經原作者的授權同意&#xff0c;將文章分享于此。原文鏈接&#xff1a;Java服務GC參數調優案例&#xff0c;下面為轉載此…

RUNOOB python 67 數組的元素互換

用來練手的Python練習題&#xff0c;原題鏈接:python練習實例67 題干: 輸入數組&#xff0c;最大的與第一個元素交換&#xff0c;最小的與最后一個元素交換&#xff0c;輸出數組 代碼如下: import numpy as nptable np.array([10,4,9,3,11,25,37,15,2,231,672,22]) #定義sw…

11.13 ethtool:查詢網卡參數

ethtool命令用于查詢或設置網卡參數。ethtool [devname][rootlinuxprobe ~]# ethtool eth0Settings for eth0:Supported ports: [ TP ]Supported link modes: 10baseT/Half 10baseT/Full 100baseT/Half 100baseT/Full 1000baseT/Full Supported pause frame use: NoSupports au…

微信小程序、微信公眾號、H5之間相互跳轉

一、小程序和公眾號 答案是&#xff1a;可以相互關聯。 在微信公眾號里可以添加小程序。 圖片有點小&#xff0c;我把文字打出來吧&#xff1a; 可關聯已有的小程序或快速創建小程序。已關聯的小程序可被使用在自定義菜單和模版消息等場景中。 公眾號可關聯同主體的10個小程…

數組元素前移后移 RUNOOB python練習題 68

用來練手的python練習題&#xff0c;原題鏈接: python練習實例68 題干: 有 n 個整數&#xff0c;使其前面各數順序向后移 m 個位置&#xff0c;最后 m 個數變成最前面的 m 個數 代碼如下: import numpy as np # 構造一個儲存了n個整數的numpy數組 def numbers_input(n):a n…

LRU緩存簡單實現

緩存接口定義 /*** 緩存接口* * author zhi**/ public interface ICache<K, V> {/*** 添加緩存數據* * param key* param value*/void put(K key, V value);/*** 獲取緩存數據* * param key* return*/V get(K key);/*** 刪除緩存數據* * param key* return*/V remove(K k…

Mac Eclipse安裝lombok

Lombok是一個可以通過注解的形式可以幫助消除一些必須但是顯得很臃腫的Java代碼的工具&#xff0c;通過使用對應的注解&#xff0c;可以在進行編譯源碼的時候生成對應的方法&#xff0c;比如類屬性的get/set/toString()/類的構造方法等. 下面記錄一下在Mac Eclipse是如何安裝Lo…