學習筆記----圖論學習中
現在在外邊實訓,事情不是很多,趕緊補一下圖論,發現自己已經落下太多的東西了啊、、、自己已經越來越差勁了啊,必須讓自己多學點啊。。。沒帶紙筆只好現在部落格裡記錄一下學習的內容,回去之後再好好的整理一下啊。
前幾天再跟著老師瞎鼓搗那個所謂的專案,現在專案做了一半了,整體已經差不多也都可以了,所以看看書。
按時間寫吧,以後也好整理:
1.10;(總結來自劉汝佳寫的大白書)還要感謝老譚的指導。
今天看的東西也不是很多,就是看了一下無向圖的割頂和橋:
首先寫一下割點的定義:就是在一個圖中如果刪除掉某些點之後會使得圖的連通分量增加,簡單的說就是以前是n個迴路,先變成n+1,或者是等多的迴路了。
最簡單的判斷一個點是不是割點的方法就是把所有的點進行dfs,然後看是否會有連通分量增加,這樣是很慢的,書上說會是O(n*(n+m))的時間複雜度,於是就有了一種更優化的演算法。
先提出了時間戳的概念:就是到達這一步的時間是多少,其實並不是真的多少分鐘多少秒,而是到達這步是第幾步了。
這種演算法的簡述就是:先求出到達這一步的時間,然後從這個點之後所有子樹中(就是說這個點之後所有可以到達這個點的點)求出到達他們之間到達的最少的時間,然後比較最少時間low和pre[u],如果low < pre[u],就是說u點之後的點通過其他的祖先可以比通過u更快的到達,那麼這就說明u點之後的某個點不通過u也可以直接到達,那麼u點就不是割點了;反之如果low >= pre[u],那就是除了這一點之後的點不能通過其他點直接到達,那麼這個點就是割點了啊,因為刪掉它之後就會導致連通分量增加。
下面附上通過劉汝佳的程式碼寫的程式碼,其實主要是老譚同學寫的我學習了一下啊。
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stack>
#include <vector>
#include <queue>
#include <map>
using namespace std;
const int maxn = 1000;
vector<int> G[maxn];
int pre[maxn], dfs_clock, low[maxn], n;
bool iscut[maxn];
void init()
{
for(int i=0; i<n; i++) G[i].clear();
memset(iscut, false, sizeof(iscut));
memset(pre, 0, sizeof(pre));
dfs_clock = 0;
}
int dfs(int u, int fa) //u在DFS樹中的父結點是fa
{
int lowu = pre[u] = ++dfs_clock;
int child = 0; //子結點數目
for(int i=0; i<G[u].size(); i++)
{
int v = G[u][i];
if(!pre[v]) //沒有訪問過v, 沒有必要用vis標記了
{
child++;
int lowv = dfs(v, u);
lowu = min(lowu, lowv); //用後代的 low 函式更新 u 的 low 函式
if(lowv >= pre[u])
{
iscut[u] = true;
}
}
else if(pre[v] < pre[u] && v != fa) //(u,v)為反向邊
{
lowu = min(lowu, pre[v]); //用反向邊更新 u 的 low 函式
}
if(fa < 0 && child == 1) iscut[u] = false;
}
low[u] = lowu;
return lowu;
}
int main()
{
int m, u, v;
scanf("%d%d", &n, &m);
init();
for(int i=0; i<m; i++)
{
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
for(int i=0; i<n; i++)
if(!pre[i])
{
dfs(i, -1);
}
for(int i=0; i<n; i++) //將割點輸出
{
if(iscut[i]) printf("%d ", i);
}
putchar('\n');
return 0;
}
實訓終於結束了啊,又在那裡不幸感冒發了小燒,一直沒怎麼再看書,所以沒寫,回來的補上啊。
下面是無向圖的點雙連通分量:
先摘劉汝佳大白書上的一段話吧:對於一個連通圖,如果任意兩點至少存在兩條“點不重複”的路徑,(這裡得說一下,一定注意要點不重複,這裡是為什麼要求出割點的原因,因為割點把它們分成了不同的塊)則說明這個圖是點聯通圖,即內部無割頂。
類似的,如果任意兩點至少存在兩條“邊不重複”的路徑,我們說這個圖是邊雙連通的。這個要求低一點,只需要每條邊至少在一個簡單環中,即所有邊都不是橋。
對於一張無向圖,點-雙連通圖,點-雙連通圖的極大子圖成為雙連通分量或塊。每條邊恰好屬於一個雙連通分量,但不同雙連通分量可能會有公共點。可以證明不同雙連通分量最多隻有一個公共點,而且一定是割頂。另一方面,任意割頂都是至少兩個不同雙連通分量的公共點。
同理邊-雙連通分量的橋與這裡的割頂是一樣的啊。
———————————————————————————————————————————————————
在下面說一下我自己的理解: 這裡是點連通圖,兩個點之間要至少有兩條路可以到達,而且這兩條路上不可以有相同的點,這才是點雙連通圖。注意了這一點就很明瞭了啊,每個割點都是一個分割點,把圖分成幾部分。注意這裡的幾部分至少有一個是圖的塊,所以每個塊中也至少會包含一個割頂。因此要想找到塊就必須先找到割頂,然後根據割頂把圖分開,把割頂相連的所有的點加入到棧當中,然後判斷所有的點所屬於的塊集,如果已經屬於別的集合的點(一般是割頂),就不加入到容器中,用vector儲存點是第幾個塊的第幾個元素。注意當出棧時有可能棧不空但是已經將割頂彈出了,就退出迴圈,有可能棧不為空。
說明一下:在演算法中每個割頂屬於哪個塊是沒有意義的。
以POJ 3352 為例貼一下程式碼:
Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 7704 | Accepted: 3874 |
Description
It's almost summer time, and that means that it's almost summer construction time! This year, the good people who are in charge of the roads on the tropical island paradise of Remote Island would like to repair and upgrade the various roads that lead between the various tourist attractions on the island.
The roads themselves are also rather interesting. Due to the strange customs of the island, the roads are arranged so that they never meet at intersections, but rather pass over or under each other using bridges and tunnels. In this way, each road runs between two specific tourist attractions, so that the tourists do not become irreparably lost.
Unfortunately, given the nature of the repairs and upgrades needed on each road, when the construction company works on a particular road, it is unusable in either direction. This could cause a problem if it becomes impossible to travel between two tourist attractions, even if the construction company works on only one road at any particular time.
So, the Road Department of Remote Island has decided to call upon your consulting services to help remedy this problem. It has been decided that new roads will have to be built between the various attractions in such a way that in the final configuration, if any one road is undergoing construction, it would still be possible to travel between any two tourist attractions using the remaining roads. Your task is to find the minimum number of new roads necessary.
Input
The first line of input will consist of positive integers n and r, separated by a space, where 3 ≤ n ≤ 1000 is the number of tourist attractions on the island, and 2 ≤ r ≤ 1000 is the number of roads. The tourist attractions are conveniently labelled from 1 to n. Each of the following r lines will consist of two integers, v and w, separated by a space, indicating that a road exists between the attractions labelled v and w. Note that you may travel in either direction down each road, and any pair of tourist attractions will have at most one road directly between them. Also, you are assured that in the current configuration, it is possible to travel between any two tourist attractions.
Output
One line, consisting of an integer, which gives the minimum number of roads that we need to add.
Sample Input
Sample Input 1 10 12 1 2 1 3 1 4 2 5 2 6 5 6 3 7 3 8 7 8 4 9 4 10 9 10 Sample Input 2 3 3 1 2 2 3 1 3
Sample Output
Output for Sample Input 1 2 Output for Sample Input 2 0題目的大意就是:
在暑假裡景區要修路,為保障遊客可以正常的遊覽,所以要修的路不能是“橋”,否則回使圖不連通了,讓你求出這個圖再修之前至少先加入多少個點,使得這個圖可以保證這個圖是點-雙連通的。
解體的思路是:求出所有的塊,然後求出所有“塊”中只有一個割點的數目然後[cnt+1]/2就是要修的路的數目。
#include <algorithm>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <iomanip>
#include <stdio.h>
#include <string>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
#define eps 1e-7
#define M 1001000
#define LL __int64
#define INF 0x3f3f3f3f
#define PI 3.1415926535898
using namespace std;
const int maxn = 1001000;
int pre[maxn], iscut[maxn], bccno[maxn], dfs_clock, bcc_cnt, n;
vector<int> G[maxn], bcc[maxn];
struct Edge
{
int u, v;
};
stack<Edge> S;
int dfs(int u, int fa)
{
int lowu = pre[u] = ++dfs_clock;
int child = 0;
for(int i = 0; i < G[u].size(); i++)
{
int v = G[u][i];
Edge e;
e.u = u;
e.v = v;
if(!pre[v])
{
S.push(e);
child ++;
int lowv = dfs(v, u);
lowu = min(lowv, lowu);
if(lowv >= pre[u])
{
iscut[u] = 1;
bcc_cnt ++;
bcc[bcc_cnt].clear();
while(1)
{
Edge x = S.top();
S.pop();
if(bccno[x.u] != bcc_cnt)
{
bcc[bcc_cnt].push_back(x.u);
bccno[x.u] = bcc_cnt;
}
if(bccno[x.v] != bcc_cnt)
{
bcc[bcc_cnt].push_back(x.v);
bccno[x.v] = bcc_cnt;
}
if(x.v == v && x.u == u)
break;
}
}
}
else if(pre[v] < pre[u] && v != fa)
{
S.push(e);
lowu = min(lowu, pre[v]);
}
}
if(fa < 0 && child == 1)
iscut[u] = 0;
return lowu;
}
void find_bcc()
{
int i;
memset(bccno , 0 , sizeof(bccno));
memset(iscut , 0 , sizeof(iscut));
memset(pre , 0 , sizeof(pre));
bcc_cnt = 0;
dfs_clock = 0;
for(i = 1; i <= n; i++)
if(!pre[i])
dfs(i, -1);
}
int main()
{
int u, v, m, i, j;
while(cin >>n>>m)
{
for(i = 1; i <= m; i++)
{
cin >>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
find_bcc();
//cout <<bcc_cnt<<endl;
int cnt = 0;
int ant = 0;
for(i = 1; i <= bcc_cnt; i++)
{
ant = 0;
for(j = 0; j < bcc[i].size(); j++)
{
if(iscut[bcc[i][j]])
ant ++;
}
if(ant == 1)
cnt++;
}
cout<<(cnt+1)/2<<endl;
}
return 0;
}
未完待續。。。。。
相關文章
- 群論學習筆記筆記
- 圖論進階學習筆記(四)(2024.10.4)圖論筆記
- [學習筆記] 樹上差分 - 圖論筆記圖論
- 機率論 學習筆記筆記
- 弦圖 學習筆記筆記
- 學習筆記:數論分塊筆記
- 數論學習筆記 (1):整除筆記
- 資訊理論理論學習筆記筆記
- [學習筆記] 丟番圖方程 & 同餘 & 逆元 - 數論筆記
- numpy的學習筆記\pandas學習筆記筆記
- 期望 與 機率論 學習筆記筆記
- 人工智慧導論學習筆記人工智慧筆記
- 數論學習筆記 (2):質數筆記
- 學習筆記:深度學習中的正則化筆記深度學習
- systemtap和火焰圖學習筆記筆記
- 機器學習學習筆記機器學習筆記
- 《JAVA學習指南》學習筆記Java筆記
- 中斷的學習筆記筆記
- Kubernetes學習筆記(更新中。。。。)筆記
- Node中Buffer學習筆記筆記
- 學習筆記筆記
- 強化學習-學習筆記3 | 策略學習強化學習筆記
- 【學習筆記】數學筆記
- 隨機過程學習筆記——概論隨機筆記
- 深度學習框架Pytorch學習筆記深度學習框架PyTorch筆記
- Vue學習筆記(二)------axios學習Vue筆記iOS
- kitten 學習教程(一) 學習筆記筆記
- 深度學習-理論學習關鍵示意圖深度學習
- 強化學習-學習筆記2 | 價值學習強化學習筆記
- 機器學習整合學習—Apple的學習筆記機器學習APP筆記
- AD學習筆記----原理圖設計筆記
- Python學習筆記 - 下載圖片Python筆記
- 【統計學習方法|筆記】第1章 統計學習方法理論筆記
- 強化學習-學習筆記14 | 策略梯度中的 Baseline強化學習筆記梯度
- 圖形學學習筆記二:觀測變換筆記
- Flutter 學習筆記:再次重新學習FlutterFlutter筆記
- 強化學習-學習筆記5 | AlphaGo強化學習筆記Go
- 深度學習 DEEP LEARNING 學習筆記(一)深度學習筆記
- 深度學習 DEEP LEARNING 學習筆記(二)深度學習筆記