Data Structure第七次作業講解


Data Structure第七次作業講解

寫給讀者的話(務必閱讀)

期中以來,有不少同學向我反應代碼寫的慢、正確率不高等問題。由於OS已經爆炸閑的沒事干 因此我決定將自己原來寫的代碼重構重構,並整理成博客附上整個思路的講解。首先,我必須申明,博客所寫的東西,是供想提升自己代碼水平,理清寫碼思路的同學用的。我希望同學們能夠明白:作為一個考試分數占 80% 的學科,抄襲他人代碼完成作業不是白賺了那 20% 的分數,而是失去了一次良好的練兵的機會(從本次開始,文章中給出的代碼均已提交評測,抄襲需謹慎)。其次,個人所寫代碼只是提供一個寫碼、思考問題的思路,並不代表題目的唯一解法,更不代表最優解法沒有提交到課程網站上測試,只在本地通過測試數據,甚至可能有bug噢。我希望我的代碼能夠起到拋磚引玉的作用,能夠讓同學對課上內容有更深刻的理解,寫代碼時能夠有更多更好的想法。最后,我希望同學們在完成作業的同時,能夠對自己的代碼進行復雜度的分析。數據結構的使用,往往離不開對性能的約束,因此,掌握復雜度的分析也是這門課程重要的一環。

關於代碼風格

本文中所有的代碼風格皆采取 OO 的標准,同時作者也希望同學們能夠以這種標准約束自己,這樣也會方便助教 debug。簡單來說,大致約束如下:

1、符號后帶空格。

2、大括號不換行。

3、if、while、for 等括號兩端應該帶空格,並且一定要用大括號括起來。

4、一行不要寫過多字符(不超過60),較長的判斷可以換行處理。

5、縮進為 4 個空格,不同層次間要有合適的縮進。

6、一行只聲明一個變量,只執行一個語句。

關於使用到的工具

采取了dhy大佬的意見,決定新添加這個欄目,對本次代碼中使用到的基礎的一些數據結構或是函數進行一些簡單的講解,便於大家的使用和理解。

1、快速讀入

inline int read() { //快速讀入,可以放在自己的缺省源里面 
	int x = 0; //數字位 
	int f = 1; //符號位 
	char ch = getchar(); //讀入第一個字符 
	while (!isdigit(ch)) { //不是數字 
		if (ch == '-') { //特判負號 
			f = -1;
		}
		ch = getchar();
	}
	while (isdigit(ch)) { //讀入連續數字 
		x = (x << 3) + (x << 1) + ch - '0'; // x * 10 == (x << 3) + (x << 1) 
		ch = getchar();
	}
	return x * f;
}

快速讀入是比較好用的一種讀入的寫法,我這里的實現是通過循環讀入直到得到下一個數字,在具體的題目中也可以根據自己的需要對循環的條件和結束條件做更改來讀入字符串等。(由於只涉及到簡單循環,這里不作更深入的講解)。切忌不經思考和理解就使用,容易出現讀入死循環等問題。

2、鏈式前向星

在解決需要建邊(如:樹、圖)相關的問題時,比較方便的一種數據結構。在第一道題中用到了類似的結構,因此我們在這里做個講解。

typedef struct edge {
	int to; //指向這條邊到達的對象
	int nxt; //指向當前表頭的下一條邊,為 0 則代表沒有
} Edge;
Edge e[maxn];
int h[maxn];
int cnt;

void adde(int x, int y) { //建一條 x 向 y 的邊
	e[++cnt] = (Edge) {y, h[x]}; //產生新的指向 y 的表頭,並將 nxt 指向之前的表頭
	h[x] = cnt; //更新表頭
}

void forEach(int x) {
	int i;
	for (i = h[x]; i; i = e[i].nxt) { //遍歷時從表頭開始依次訪問每個邊,取出其指向的點
		int y = e[i].to;
		forEach(y);
	}
}

如此圖所示,我們新加入編號為 3 的邊

初始狀態 e[1] = {2, 0} (前面為to,后面為nxt)

​ e[2] = {3, 1}

​ h[1] = 2

這時我們新建 e[3] = {4, h[1]} 即 {4,2}

再令 h[1] = 3

那么遍歷的時候我們可以通過 h[1] 找到 e[3],再通過 e[3].nxt 找到 e[2], 再通過 e[2].nxt 找到 e[1],就獲得了完整的邊的信息。

3、並查集

一種多用在解決集合關系中的算法,實現 kruskal 所需要的工具。

簡單來講,就是維護一個 prt 數組。

當 prt[i] = i 時,代表這個點就是集合的代表元素。否則一直往上找到一個這樣的點。

連接兩個點 x,y 就是分別找到他們的代表元素 f1 和 f2

如果 f1 == f2 ,則代表兩個點已經在同一集合中,不需要更改並查集

否則令 prt[f1] = f2 ,完成建邊。

int getf(int x) { //找到最上面的父親 
	return x == prt[x] ? x : (prt[x] = getf(prt[x]));
} 
//注意這里可以看見,將prt[x]改為了最上面的父親,這是為了優化時間復雜度。這里就不贅述,想知道的同學可以課上問我。
void combine(int x, int y) {
	int f1 = getf(x);
 	int f2 = getf(y);
    	if (f1 == f2) {
        	return;
    	}
    	prt[f1] = f2;
}

void pre() { //注意初始化時每個點單獨是一個集合
	int i;
    	for (i = 1; i <= n; ++i) {
        	prt[i] = i;
    	}
}

4、SPFA

一種最短路算法,思路簡單暴力,實現簡單,復雜度優秀(如果沒有被數據針對的話)

整體架構大致如下:

我們用一個 vst 數組記錄點在不在隊列中。

如果一個點的最短路被更新時不在隊列中,就把它加入隊列中

本質上就是對暴力BFS的優化 是不是很簡單

while (size()) { // SPFA
		int x = front();
		pop();
		vst[x] = 0;
		int i;
		for (i = h[x]; i; i = e[i].nxt) {
			int y = e[i].to;
			if (dis[y] > dis[x] + e[i].w) {
				dis[y] = dis[x] + e[i].w;
				if (!vst[y]) {
					push(y); 
					vst[y] = 1; 
				} 
			}
		}
	}

第一題:圖遍歷(圖-基本題)

題目描述

【問題描述】

給定一個無向圖和一個圖頂點,編程輸出該圖刪除給定頂點前后按深度優先遍歷及廣度優先遍歷方式遍歷的圖頂點序列。

給定的無向圖和圖頂點滿足以下要求:

1、無向圖的頂點個數n大於等於3,小於等於100,輸入時頂點編號用整數0~n-1表示;

2、無向圖在刪除給定頂點前后都是連通的;

3、無論何種遍歷,都是從編號為0的頂點開始遍歷,訪問相鄰頂點時按照編號從小到大的順序訪問;

4、刪除的頂點編號不為0。

【輸入形式】

先從標准輸入中輸入圖的頂點個數和邊的個數,兩整數之間以一個空格分隔,然后從下一行開始分行輸入每條邊的信息(用邊兩端的頂點編號表示一條邊,以一個空格分隔頂點編號,邊的輸入次序和每條邊兩端頂點編號的輸入次序可以是任意的,但邊不會重復輸入),最后在新的一行上輸入要刪除的頂點編號。

【輸出形式】

分行輸出各遍歷頂點序列,頂點編號之間以一個空格分隔。先輸出刪除給定頂點前的深度優先遍歷頂點序列和廣度優先遍歷頂點序列,再輸出刪除給定頂點后的深度優先遍歷頂點序列和廣度優先遍歷頂點序列。

【樣例輸入】

9 10

0 1

0 2

1 4

1 3

1 8

8 6

3 6

7 2

7 5

5 2

3

【樣例輸出】

0 1 3 6 8 4 2 5 7

0 1 2 3 4 8 5 7 6

0 1 4 8 6 2 5 7

0 1 2 4 8 5 7 6

【樣例說明】

輸入的無向圖有9個頂點,10條邊(如下圖所示),要刪除的頂點編號為3。

graph.jpg

從頂點0開始,按照深度優先和廣度優先遍歷的頂點序列分別為:0 1 3 6 8 4 2 5 7和0 1 2 3 4 8 5 7 6。刪除編號為3的頂點后,按照深度優先和廣度優先遍歷的頂點序列分別為:0 1 4 8 6 2 5 7和0 1 2 4 8 5 7 6。

題目大意

按照題目要求從 0 開始進行 dfs 和 bfs

題目思路

這個題要求遍歷按照編號的順序,因此我們用鄰接矩陣來存再枚舉可以自動滿足這個要求

除此之外我們需要一個vst數組來記錄訪問了哪些點

然后就正常進行 bfs 和 dfs 即可

代碼實現

#include <stdio.h> 
#include <math.h>
#include <string.h>
#include <ctype.h>

inline int read() { //快速讀入,可以放在自己的缺省源里面 
	int x = 0; //數字位 
	int f = 1; //符號位 
	char ch = getchar(); //讀入第一個字符 
	while (!isdigit(ch)) { //不是數字 
		if (ch == '-') { //特判負號 
			f = -1;
		}
		ch = getchar();
	}
	while (isdigit(ch)) { //讀入連續數字 
		x = (x << 3) + (x << 1) + ch - '0'; // x * 10 == (x << 3) + (x << 1) 
		ch = getchar();
	}
	return x * f;
}

#define BIG 1000005
int q[BIG]; //為 BFS 提供隊列操作的各種函數 
int head, tail;

void clear() { //清空隊列 
	head = 1;
	tail = 0;
}

int front() { //取出隊首 
	return q[head];
} 

void pop() { //彈出隊首 
	++head;
}

void push(int x) { //放值進入隊尾 
	q[++tail] = x;
}

int size() { //獲得隊列大小 
	return tail - head + 1;
}

#define maxn 105
int n, m;
int map[maxn][maxn]; //鄰接矩陣記錄路徑 
int del[maxn]; //刪除標記 
int vst[maxn]; //記錄已經訪問的結點 

void dfs(int x) {
	printf("%d ", x);
	vst[x] = 1;
	int i;
	for (i = 0; i < n; ++i) {
		if (!map[x][i] || del[i] || vst[i]) { //沒有邊,刪除,或者訪問過 
			continue;
		}
		dfs(i); //繼續深搜 
	}
}

void bfs(int x) {
	clear();
	push(x);
	vst[x] = 1;
	printf("%d ", x);
	int i;
	while (size()) {
		int x = front(); //每次取出隊首 
		pop();
		for (i = 0; i < n; ++i) {
			if (!map[x][i] || del[i] || vst[i]) { //沒有邊,刪除,或者訪問過 
				continue;
			}
			vst[i] = 1;
			printf("%d ", i);
			push(i); //將新的結點放入隊列 
		}
	}
}

int main() {
	n = read();
	m = read();
	int i;
	for (i = 1; i <= m; ++i) {
		int x = read();
		int y = read();
		map[x][y] = map[y][x] = 1;
	} 
	memset(vst,0,sizeof vst);
	dfs(0);
	puts("");
	memset(vst,0,sizeof vst);
	bfs(0);
	puts("");
	del[read()] = 1; //讀入被刪除的點 
	memset(vst,0,sizeof vst);
	dfs(0);
	puts("");
	memset(vst,0,sizeof vst);
	bfs(0);
	puts("");
	return 0;
} 

復雜度分析

由於使用了鄰接矩陣,每個點都遍歷了一次別的點, 復雜度是 O(n²) 的

第二題:獨立路徑數計算

題目描述

【問題描述】

老張和老王酷愛爬山,每周必爬一次香山。有次兩人為從東門到香爐峰共有多少條路徑發生爭執,於是約定一段時間內誰走過對方沒有走過的路線多誰勝。

給定一線路圖(無向連通圖,兩頂點之間可能有多條邊),編程計算從起始點至終點共有多少條獨立路徑,並輸出相關路徑信息。

注:獨立路徑指的是從起點至終點的一條路徑中至少有一條邊是與別的路徑中所不同的,同時路徑中不存在環路。

img

【輸入形式】

圖的頂點按照自然數(0,1,2,...,n)進行編號,其中頂點0表示起點,頂點n-1表示終點。從標准輸入中首先輸入兩個正整數n,e,分別表示線路圖的頂點的數目和邊的數目,然后在接下的e行中輸入每條邊的信息,具體形式如下:

<n> <e>

<e1> <vi1> <vj1>

<e2> <vi2> <vj2>

...

<en> <vin> <vjn>

說明:第一行<n>為圖的頂點數,<e>表示圖的邊數;第二行<e1> <vi1> <vj1>分別為邊的序號(邊序號的范圍在[0,1000)之間,即包括0不包括1000)和這條邊的兩個頂點(兩個頂點之間有多條邊時,邊的序號會不同),中間由一個空格分隔;其它類推。

【輸出形式】

輸出從起點0到終點n-1的所有路徑(用邊序號的序列表示路徑且路徑中不能有環),每行表示一條由起點到終點的路徑(由邊序號組成,中間有一個空格分隔,最后一個數字后跟回車),並且所有路徑按照字典序輸出。

【樣例輸入】

6 8

1 0 1

2 1 2

3 2 3

4 2 4

5 3 5

6 4 5

7 0 5

8 0 1

【樣例輸出】

1 2 3 5

1 2 4 6

7

8 2 3 5

8 2 4 6

【樣例說明】

樣例輸入構成的圖如下:

graph8_1.png

輸出的第一個路徑1 2 3 5,表示一條路徑,先走1號邊(頂點0到頂點1),然后走2號邊(頂點1到頂點2),然后走3號邊(頂點2到頂點3),然后走5號邊(頂點3到頂點5)到達終點。

題目大意

輸出所有從 0 到 n - 1 的簡單路徑,並且還要按照字典序。

題目思路

顯然一個 bfs 或者 dfs 我們就能得到所有這樣的路徑,但這樣終究有點麻煩。

我們只要對邊排好序,其實訪問到可行的路徑自動就是一個字典序的。

之后只要在 dfs 過程中用棧來維護答案就可以了。

代碼實現

#include <stdio.h> 
#include <math.h>
#include <string.h>
#include <ctype.h>

inline int read() { //快速讀入,可以放在自己的缺省源里面 
	int x = 0; //數字位 
	int f = 1; //符號位 
	char ch = getchar(); //讀入第一個字符 
	while (!isdigit(ch)) { //不是數字 
		if (ch == '-') { //特判負號 
			f = -1;
		}
		ch = getchar();
	}
	while (isdigit(ch)) { //讀入連續數字 
		x = (x << 3) + (x << 1) + ch - '0'; // x * 10 == (x << 3) + (x << 1) 
		ch = getchar();
	}
	return x * f;
}

#define BIG 1000005
#define maxn 1005

typedef struct line{
	int to, bh;
} Line;

Line l[BIG];
int n, m, cnt;
int edge[maxn][maxn]; //記錄每個點連向哪些邊 
int edge_num[maxn]; //記錄每個點連向邊的數目 

int cmp(const void *a, const void *b) {
	int *p1 = (int*) a;
	int *p2 = (int*) b;
	return l[*p1].bh - l[*p2].bh;
}

int vst[maxn];
int sta[BIG]; // 用數組實現棧 
int top;

void dfs(int x) {
	vst[x] = 1; //標記這條路徑上已經經過了 x 結點 
	int i;
	if (x == n-1) { //遍歷到了所需結點 
		for (i = 1; i <= top; ++i) {
			printf("%d ", sta[i]);
		}
		puts("");
		vst[x] = 0;
		return;
	}
	for (i = 0; i < edge_num[x]; ++i) {
		int y = l[edge[x][i]].to;
		if (!vst[y]) { //沒有遍歷到 y 結點 
			sta[++top] = l[edge[x][i]].bh;
			dfs(y);
			--top;
		}
	}
	vst[x] = 0;
}

int main() {
	n = read();
	m = read();
	int i;
	for (i = 1; i <= m; ++i) { //讀入並保存邊信息 
		int e = read();
		int x = read();
		int y = read();
		l[++cnt] = (Line) {y, e}; 
		edge[x][edge_num[x]++] = cnt;
		l[++cnt] = (Line) {x, e}; 
		edge[y][edge_num[y]++] = cnt;
	} 
	for (i = 0; i < n; ++i) { //排序保證邊按字典序 
		qsort(edge[i], edge_num[i], sizeof(int), cmp);
	}
	dfs(0);
	return 0;
} 

復雜度分析

由於涉及到路徑的復制,且路徑數隨邊數變化較大,復雜度可以非常大,但是由於邊數限制,這種做法沒有問題。

第三題:最少布線(圖)

題目描述

【問題描述】

北航主要辦公科研樓有新主樓、逸夫樓、如心樓、辦公樓、圖書館、主樓、一號樓等等;。北航網絡中心計划要給相關建築物間鋪設光纜進行網絡連通,請給出用料最少的鋪設方案。

編寫程序輸入一個辦公區域分布圖及建築物之間的距離,計算出用料最少的鋪設方案(只有一組最優解,不用考慮多組解)。要求采用Prim或Kruskal算法實現。

【輸入形式】

辦公區域分布圖的頂點(即建築物)按照自然數(0,1,2,n-1)進行編號,從標准輸入中首先輸入兩個正整數,分別表示線路圖的頂點的數目和邊的數目,然后在接下的行中輸入每條邊的信息,每條邊占一行,具體形式如下:

...

即頂點vi和vj之間邊的權重是weight,邊的編號是id。

【輸出形式】

輸出鋪設光纜的最小用料數,然后另起一行輸出需要鋪設的邊的id,並且輸出的id值按照升序輸出。

【樣例輸入】

6 10

1 0 1 600

2 0 2 100

3 0 3 500

4 1 2 500

5 2 3 500

6 1 4 300

7 2 4 600

8 2 5 400

9 3 5 200

10 4 5 600

【樣例輸出】

1500

2 4 6 8 9

【樣例說明】

樣例輸入說明該分布圖有6個頂點,10條邊;頂點0和1之間有條邊,邊的編號為1,權重為600;頂點0和2之間有條邊,權重為100,其它類推。其對應圖如下:

graph8_1.png

經計算此圖的最少用料是1500,可以使圖連通,邊的編號是2 4 6 8 9。其對應的最小生成樹如下:

graph8_3.png

題目大意

求最小生成樹

題目思路

出於簡單,我們當然使用 kruskal 實現這道題

同時為了實現這道題,我們需要用到並查集(見前文)。

代碼實現

#include <stdio.h> 
#include <math.h>
#include <string.h>
#include <ctype.h>

inline int read() { //快速讀入,可以放在自己的缺省源里面 
	int x = 0; //數字位 
	int f = 1; //符號位 
	char ch = getchar(); //讀入第一個字符 
	while (!isdigit(ch)) { //不是數字 
		if (ch == '-') { //特判負號 
			f = -1;
		}
		ch = getchar();
	}
	while (isdigit(ch)) { //讀入連續數字 
		x = (x << 3) + (x << 1) + ch - '0'; // x * 10 == (x << 3) + (x << 1) 
		ch = getchar();
	}
	return x * f;
}

#define maxn 100005

typedef struct line {
	int id;
	int x;
	int y;
	int v;
} Line;

Line l[maxn];
int n;
int m;
int ans[maxn];
int prt[maxn];
int cnt;

int sort_value(const void *a, const void *b) { //按邊權從小到大排 
	Line* p1 = (Line*) a;
	Line* p2 = (Line*) b;
	return p1->v - p2->v;
}

int sort_id (const void *a, const void *b){ //對 id 排序 
	int *p1 = (int*) a;
	int *p2 = (int*) b;
	return *p1 - *p2;
}

int getf(int x) { //找到最上面的父親 
	return x == prt[x] ? x : (prt[x] = getf(prt[x]));
} 

void solve() {
	qsort(l, m, sizeof(Line), sort_value); //先按照邊權給邊排序
	int i;
	int tot = 0;
	for (i = 0; i < n; ++i) { //並查集初始化 prt 
		prt[i] = i;
	} 
	for (i = 0; i < m; ++i) {
		int f1 = getf(l[i].x); 
		int f2 = getf(l[i].y);
		if (f1 == f2) { //已經在一個集合中
			continue; 
		}
		ans[cnt++] = l[i].id; //當前邊加入邊集 
		tot += l[i].v; //計算總和 
		prt[f1] = f2; //更新並查集 
	}
	printf("%d\n", tot);
	qsort(ans, cnt, sizeof(int), sort_id); //輸出時對 id 排序 
	for (i = 0; i < cnt; ++i) {
		printf("%d ", ans[i]);
	}
}


int main() {
	n = read();
	m = read();
	int i;
	for (i = 0; i < m; ++i) {
		l[i].id = read();
		l[i].x = read();
		l[i].y = read();
		l[i].v = read(); 
	}
	solve();
	return 0;
} 

復雜度分析

復雜度是 O(mlogm),即邊排序的復雜度。

第四題:北京地鐵乘坐線路查詢

題目描述

【問題描述】

編寫一個程序實現北京地鐵最短乘坐(站)線路查詢,輸入為起始站名和目的站名,輸出為從起始站到目的站的最短乘坐站換乘線路。注:1. 要求采用Dijkstra算法實現;2)如果兩站間存在多條最短路徑,找出其中的一條就行。

img

【輸入形式】

文件bgstations.txt為數據文件(可從課程網站中課程信息處下載),包含了北京地鐵的線路及車站信息。其格式如下:

<地鐵線路總條數>

<線路1> <線路1站數>

<站名1> <換乘狀態>

<站名2> <換乘狀態>

...

<線路2> <線路2站數>

<站名1> <換乘狀態>

<站名2> <換乘狀態>

...

說明:文件第一行為地鐵總線路數;第二行第一個數為某條地鐵線線號(如,1為1號線),第二個數為該條地鐵線的總站數(如1號線共有23站),兩數之間由一個空格分隔;第三行兩個數據分別為地鐵站名及換乘狀態(0為非換乘站,1為換乘站),兩數據間由一個空格分隔;以下同,依次為該線地鐵其它站信息。在一條線路信息之后是下條地鐵線路信息,格式相同。若某條地鐵線為環線,則首站與末站信息相同(如北京地鐵2號線,首站信息“西直門 1” ,末站信息為“西直門 1”)。例如本題提供的bgstations.txt文件(可從課程網站中課程信息處下載)內容如下:

13

1 23

蘋果園 0

古城 0

八角游樂園 0

八寶山 0

玉泉路 0

五棵松 0

萬壽路 0

公主墳 1

軍事博物館 1

木樨地 0

南禮士路 0

復興門 1

西單 1

...

2 19

西直門 1

積水潭 0

鼓樓大街 1

...

西直門 1

...

題目大意

在北京地鐵的背景下,用 dijkstra 實現最短路並且將路徑輸出。

其實並沒有用 dijkstra 而是用了 SPFA,具體見上文。

題目思路

這個題似乎存在不是換乘站但是同時在兩條線路上的點,但是由於不涉及到測試數據,這里就不考慮了因為考慮了我也錯了

真要考慮的話,可能需要使用分層圖來實現這道題,這就留給同學們自行了解了。

大體思路是利用鏈式前向星從 2 開始編號,異或得到相反邊的性質來保存整個路徑,最后處理輸出。

最短路是用 SPFA 實現的,整個題目不簡單,請仔細閱讀代碼並深入思考。

代碼實現

#include<stdio.h> 
#include<math.h>
#include<string.h>
#define maxn 2005

struct point{
	char s[30];
} p[maxn];

int tot;

int newnode(char *s) { //新建結點 
	++tot;
	strcpy(p[tot].s, s);
	return tot;
}

int check(char *s) { //檢查當前站是否在之前出現過,並返回編號 
	int i;
	for (i = 1; i <= tot; ++i) {
		int t = strcmp(p[i].s, s);
		if (t == 0) {
			return i;
		}
	}
	return newnode(s);
} 

#define maxm 100005
typedef struct line{ //借助鏈式前向星來組織圖結構 
	int to;
	int nxt;
	int bh;
} Line;

Line e[maxm];
int h[maxn];
int cnt = 1;

void adde(int x, int y, int bh) { //cnt 從 2 開始編號,便於后續操作 
	e[++cnt] = (Line){y, h[x], bh};
	h[x] = cnt;
}

int q[maxn]; //為 SPFA 實現各種隊列操作 
int head;
int tail;

void clear() { //清空隊列 
	head = 1;
	tail = 0;
}

int front() { //取出隊首 
	return q[head];
} 

void pop() { //彈出隊首 
	++head;
}

void push(int x) { //放值進入隊尾 
	q[++tail] = x;
}

int size() { //獲得隊列大小 
	return tail - head + 1;
}

#define INF 1e9
int vst[maxn];
int dis[maxn];
int from[maxn];

void Print(int s,int t){
	int x = s;
	int num = 0;
	int lst = -1;
	while (x != t) { //未到達目的地 
		int to = from[x]; //取出 x 記錄的邊 
		if(e[to].bh != lst){ //bh 出現變化,說明出現換乘 
			if (num) { //num有值,說明在這條線上走過(特判起始) 
			 	printf("-%d(%d)-", lst, num);
			}
			printf("%s", p[x].s); //輸出換乘站名 
			num=0;
			lst=e[to].bh;
		}
		++num;
		x=e[to].to; //在記錄的邊上前進 
	}
	if(num) { //特判最后一條線 
		printf("-%d(%d)-", lst, num);
	}
	printf("%s",p[t].s); //輸出最后一站站名 
}

void solve() {
	char temp[30];
	scanf("%s", temp);
	int s = check(temp);
	scanf("%s", temp);
	int t = check(temp); //獲得起點終點
	int i;
	for (i = 1; i <= tot; ++i) { //初始化距離為無窮大 
		dis[i] = INF;
	} 
	dis[t] = 0; 
        clear();
	push(t); //從 t 開始反向找,便於找到路徑 
	vst[t] = 1;
	while (size()) { // SPFA
		int x = front();
		pop();
		vst[x] = 0;
		int i;
		for (i = h[x]; i; i = e[i].nxt) {
			int y = e[i].to;
			if (dis[y] > dis[x] + 1) {
				dis[y] = dis[x] + 1;
				from[y] = i ^ 1; //編號從 2 開始,異或可以得到反向邊( 
				if (!vst[y]) {
					push(y); 
					vst[y] = 1; 
				} 
			}
		}
	}
	Print(s, t);
}

int main() {
	FILE* IN = fopen("bgstations.txt", "r");
	int T;
	fscanf(IN, "%d", &T);
	int bh;
	int m;
	while (T--) {
		fscanf(IN, "%d%d", &bh, &m); //站編號及個數
		char s[30];
		int ty;
		int lst = -1; //記錄上一個站
		int nw;
		int i;
		for (i = 1; i <= m; ++i)  {
			fscanf(IN, "%s%d", s, &ty);
			nw = check(s);
			if(~lst) { //取反操作,等價於 lst != -1 
				adde(nw, lst, bh); //雙向邊 
				adde(lst, nw, bh);
			}
			lst=nw;
		}
	}
	solve();
	return 0;
}

復雜度分析

復雜度是 SPFA 的復雜度,會隨着圖的性質變化, 最壞是 O(VE) //點集 * 邊集

搖起來

大巴黎,咚咚咚


免責聲明!

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



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