Dijkstra複雜應用-解決公交線路的實際問題(SDU計科數據結構大課設)(算法思路及c++實現)

前言

由於疫情原因在家混吃等死的SDU計科學子,迎來了又一個需要熬夜肝的課程–數據結構課程設計。儘管前期的競賽樹、二叉樹森林轉換、高性能跳錶寫的我痛不欲生,但是不可否認的是,數據結構這門課的確如很多大佬所說,是一門有無限可能的課程,利用好數據結構進行開發絕非學好表面知識這麼簡單且快速的過程。要想將所有數據結構爛熟於心,使用時如應激反應一樣的快速且精準,還有很長的路要走。
好了下面進入正題,由於本文與Dijkstra算法高度相關,建議還不瞭解或者忘記了Dijkstra細節的同學移步這邊傳送門,瞭解了Dij基本使用場景和算法細節之後繼續閱讀。

題目敘述

題目簡述
最短路徑問題是圖論中的一個經典問題,其中的Dijkstra算法一直被認爲是圖論中的好 算法,但有的時候需要適當的調整Dijkstra算法才能完成多種不同的優化路徑的查詢。
對於某城市的公交線路,乘坐公交的顧客希望在這樣的線路上實現各種優化路徑的查詢。 設該城市的公交線路的輸入格式爲: 線路編號:起始站名(該站座標);經過的站點1名(該站座標);經過的站點2名(該站坐 標);……;經過的站點n名(該站座標);終點站名(該站座標)。該線路的乘坐價錢。該線路平 均經過多少時間來一輛。車速。
例如:63:A(32,45);B(76,45);C(76,90);……;N(100,100)。1元。5分鐘。1/每分鐘。 假 定線路的乘坐價錢與乘坐站數無關,假定不考慮公交線路在路上的交通堵塞。對這樣的公交 線路,需要在其上進行的優化路徑查詢包括:任何兩個站點之間最便宜的路徑;任何兩個站 點之間最省時間的路徑等等。
基本要求
① 根據上述公交線路的輸入格式,定義並建立合適的圖模型。
② 針對上述公交線路,能查詢獲得任何兩個站點之間最便宜的路徑,即輸入站名S, T後, 可以輸出從S到T的最便宜的路徑,輸出格式爲:線路x:站名S,…,站名M1;換乘線路x: 站名M1,…,站名M2;…;換乘線路x:站名MK,…,站名T。共花費x元。
③ 針對上述公交線路,能查詢獲得任何兩個站點之間最省時間的路徑(不考慮在中間站 等下一輛線路的等待時間),即輸入站名S,T後,可以輸出從S到T的考慮在中間站等下一 輛線路的等待時間的最省時間的路徑,輸出格式爲:線路x:站名S,…,站名M1;換乘線 路x:站名M1,…,站名M2;…;換乘線路x:站名MK,…,站名T。共花費x時間。
④ 針對上述公交線路,能查詢獲得任何兩個站點之間最省時間的路徑(要考慮在中間站 等下一輛線路的等待時間),即輸入站名S,T後,可以輸出從S到T的考慮在中間站等下一 輛線路的等待時間的最省時間的路徑,輸出格式爲:線路x:站名S,…,站名M1;換乘線 路x:站名M1,…,站名M2;…;換乘線路x:站名MK,…,站名T。共花費x時間。 實現提示

題目重述和分析

題目條件分析

題目是瞎眼可見的圖論算法,看完第一遍你可能會覺得:哎好簡單這個題!
如果作爲一道程序設計ACM方向的題,暴力Dij直接實現可能容易的很,但是考慮到與數據結構這門課的聯繫性,不可繞開的兩個話題便是:封裝性和複用性。我們不妨先將完善這兩個特點作爲前提,重新審視整道題目:
題目的場景是一個二維圖,每個點都用x,y兩個座標,現給出n趟公交線路,每趟公交線路設計幾個因素:站點(二維座標點)、票價、車速、等待時間。其中站點信息和車速共同標識了一個隱含信息—路途時間。
根據題目的要求和常識,站點的座標、票價、等待時間、車速都爲整數型,考慮到數值的大小,使用int存放即可。但根據距離的計算公式
sqrt[(x1-x2)2+(y1-y2)2]
利用數學上的兩點間計算公式計算出的實際距離很有可能是小數,爲了方便後續的操作,我們將double型的距離與int型的速度值做除法後,結果得到int型的時間(向上取整)。雖然該算法計算出的結果和真實值略有差距但是該誤差不影響後續算法。

題目的問題分析

題目給出的問題有三個,但是都與最短路算法息息相關:
1、在不考慮等車基礎上(換乘立刻完成),計算最短時間和路線
2、找到一條花費車票費用最少的路線
3、在考慮等車時間的基礎上,計算最短時間的路線
對上述三個問題的題面和要求分析,我們發現問題求解的幾個要點:
1、求解的路線是兩個座標之間的路線,也就是單源最短路,確定了Dij的可行性
2、要求在輸出最短路線的同時,輸出路徑上經過的各個車站和換乘路線。這一條是非常重要的,瞭解了這個要求在設計算法是就要注意路徑的回溯問題和換乘線路的記錄。
3、根據實際情況,政府一般不會將多條重複路徑安排在不同的線路上,因此在設計算法是可以假設不同的線路其站點路徑無重複。如果使用者在使用者需要使用帶有重複路徑的數據集,可以修改較小的數據存儲保證可行性。

題目設計

根據上述的問題分析,我們可以將整個問題分爲兩個部分:
1、路線圖的存儲
2、在路線圖上設計各種Dij及其變形算法

ADT設計思路簡述

整條的路線一般是不容易進行存放的,但是我們可以將整條的路線拆解成多段路徑,在每一段路徑進行存儲時,使用線路id來做以區分。
利用這種線路拆解的思路,將存放一整條路線的問題轉換成了存放散邊和一些關於邊的信息的問題。這種存邊的實現是在圖論部分中多次使用的鄰接表。
鄰接表實現
鄰接表兩種實現原理相似,主要差別在數據的存儲組織方式上:
數組&&vector
實現方式:使用vector存放每個點出發的邊信息,使用vector指針來管理全部vector從而實現數據的存儲和查找。
優點:實現容易,查找、添加操作均可調用C++的vector實例直接使用即可。且由於使用了vector的存儲方式,數據在存儲時多爲連續空間存放,連續訪問某個節點的數據時,vector內存池能夠提供很好的查找性能。
缺點:由於所有操作都需要通過vector調用類方法,時間性能較差,在處理大量數據時容易造成比較大的時間損失。
鏈表
實現方式:使用頭結點指針來管理每個點起始的前置節點指針,通過前置節點指針來訪問全部數據。
優點:所有使用的存儲空間都通過動態申請的方式獲得,且所有節點的訪問都使用指針直接訪問,運行速度快。
經過上述的兩種分析,我們已經完成了圖存儲的框架,但是由於該圖的點是二維點,就意味着上述的每種存放方式都要經過一定的處理保證存放正確:
vector存放方式:注意管理vector的指針爲一個二維指針,通過(x,y)–>vector[i][j],映射到相應的vector中。
鏈表實現方式:進行前置節點管理的指針爲二維指針,通過(x,y)–>p[i][j],映射到相應的前置節點指針地址。
存放線路時間
完成了上面的ADT設計,關於圖的基本計算已經可以實現,但是由於需求三要計算帶有等待時間的最短時間線路。考慮到題目所給的條件只有發車的間隔,且直接使用發車間隔作爲換成的等待時間並不合理。一種更加合理的算法是:計算出所有線路從兩個端點站發車到每個車站所需要的時間,結合發車的間隔,利用這兩個量就可以完成第三問的要求,具體的實現細節在問題實現部分展開。
考慮到每個站點可能有多條線路匯聚,且每條線路都是雙向行駛的,設計的數據結構要能夠保證區分開線路的同時,區分開線路的行駛方向。
經過和隊友的討論設計,我們採取瞭如下的存儲方案:
對地圖上的每個座標點(x,y),使用map存儲其有關形式時間的信息:

map<int, pair<int, int>> record;

map的key項是線路id,每一個線路id都能夠唯一標識一個pair<int,int>數據,在pair中存放了正向行駛到該站點的時間pair->first和逆向行駛到該站點的時間pair->second。因爲在輸入數據時,輸入的線路是站點信息,並不帶有正向和逆向標識,因此在線路圖的存儲中也需要添加信息:

int direct;

使用direct來標識每條邊的正向和逆向,方便計算使用,規定按照輸入順序行駛的方向即爲正向線路,反之即爲逆向線路。
同時爲了儘可能的提高後續查發車間隔的時間性能,採用map來存放不同id對應的發車間隔。考慮到map底層的紅黑樹結構,查效率可以優化至O(logn)。(這裏還可以進一步優化,因爲數據的非順序性,可以使用hashmap,將查效率進一步優化到O(1),哈希大法好!)

map<int, int> wait_map;//存放每趟路線的發車間隔

完成了上述的設計後,數據的初始化已經可以完成,添加路線的封裝函數設計如下:

void add_line(vector<pair<int, int>>* station, int speed, int value,int wait_time,int id)
	{
		route_num++;
		wait_map[id] = wait_time;
		for (int i = 0; i < (station->size() - 1); i++)
		{
			int x1 = station->at(i).first;
			int y1 = station->at(i).second;
			int x2 = station->at(i + 1).first;
			int y2 = station->at(i + 1).second;
			edge * plist = graph_p[x1].vertex[y1].first_edge;//指向頭結點
			edge* new_node = new edge();
			new_node->initialize(x1, y1, x2, y2, id, value, speed,0);//車正向行駛的邊標號爲0
			new_node->next = plist;
			graph_p[x1].vertex[y1].first_edge = new_node;
			
		}
		for (int i = station->size() - 1; i >= 1; i--)
		{
			int x1 = station->at(i).first;
			int y1 = station->at(i).second;
			int x2 = station->at(i - 1).first;
			int y2 = station->at(i - 1).second;
			edge * plist = graph_p[x1].vertex[y1].first_edge;//指向頭結點
			edge* new_node = new edge();
			new_node->initialize(x1, y1, x2, y2, id, value, speed,1);//車逆向行駛的邊標號爲1
			new_node->next = plist;
			graph_p[x1].vertex[y1].first_edge = new_node;
		}
		int start_x = station->at(0).first;
		int start_y = station->at(0).second;
		int end_x = station->at(station->size() - 1).first;
		int end_y = station->at(station->size() - 1).second;
		double a = (start_x - end_x) * (start_x - end_x) + (start_y - end_y) * (start_y - end_y);
		int route_dis = (int)sqrt(a);
		int route_time = (route_dis / speed) + 1;
		point_map[start_x][start_y].insert_time(id, 0, route_time);
		point_map[end_x][end_y].insert_time(id, route_time, 0);
		int route_time_plus = 0;
		int route_time_minus = 0;
		for (int i = 1; i < station->size() - 1; i++)
		{
			int dx = station->at(i).first;
			int dy = station->at(i).second;
			a = (dx - start_x) * (dx - start_x) + (dy - start_y) * (dy - start_y);
			route_dis = (int)sqrt(a);
			route_time_plus = (route_dis / speed) + 1;
			a = (dx - end_x)*(dx - end_x) + (dy - end_y)*(dy - end_y);
			route_dis = (int)sqrt(a);
			route_time_minus = (route_dis / speed) + 1;
			point_map[dx][dy].insert_time(id, route_time_plus, route_time_minus);
		}
	}

鏈表實現路線圖ADT源碼

struct edge
{
	pair<int, int> begin_point;//起點
	pair<int, int> des_point;//終點
	int id;
	int time;
	int cost;
	int direct;
	edge* next;//指向下一個
}
struct point_list
{
	map<int, pair<int, int>> record;
};

struct y_list//標識每個定點y座標的結構體
{
	edge* first_edge;
};

struct x_list
{
	y_list* vertex;
};

class Graph

{
private:
	int vertex_num;//出發點的個數
	int route_num;//路徑的條數
	x_list* graph_p;//存圖
	map<int, int> wait_map;//存放每趟路線的發車間隔
	point_list** point_map;//存放每個點的到每一個始發站和終點站的時間
}

具體問題實現

完成了上述的ADT設計,數據的存儲和初始化都可以順理成章的完成了,下面需要針對具體的問題進行具體的分析並給出問題的具體求解思路。Dij算法的核心在於鬆弛算法的鬆弛原則和具體鬆弛操作的設計,本部分將圍繞這兩個主題進行。

需求1

需求1的要求是實現不考慮等待時間的最短時間路線設計。顯而易見的是,由於求解的是最短時間問題,每個點不可能被重複經過,否則兩次重複經過之間的時間是浪費掉的。
由於沒有其他因素影響結果,可以將路線行駛花費時間直接作爲Dij的參照因素,使用基本的Dij算法實現即可。但是因爲要輸出行駛線路的站點和換乘路線,需要在跑Dij算法的同時,維護兩個數據結構存儲上述兩種信息。

int** route_id = NULL;//路線id的前置矩陣
pair<int, int>** front_point = NULL;//前置節點的訪問矩陣

front_point記錄了每個點的前置節點,把起始點的前置節點置爲空即可。在輸出路徑是,從終點回溯並將各點加入vector,完成後倒序輸出即可。
route_id記錄了到每個點所經過的路線id,其中起始點的id=0,在輸出路徑id時,比對前後兩條線路的id,如果相同說明仍然在該線路上行駛,如果不相同說明換乘了其他線路,輸出換乘的id號即可。
具體的算法流程如下:
1、初始化數據結構
2、維護一個小根堆,小根堆的key是到源點需要的時間。將源點加入小根堆
3、取出小根堆的頭結點,遍歷其鄰接邊,並做鬆弛操作。
鬆弛原則
如果源點行駛到該邊起點時間+該邊行駛時間<源點行駛到該邊終點時間,進行鬆弛。
鬆弛操作
1、將時間數組更新
2、將終點加入小根堆
3、更新終點的前置節點數組
4、更新終點的前置路線id數組
5、更新當前行駛的id號

if (dis[des_x][des_y] > dis[dx][dy] + p->time)
	{
		dis[des_x][des_y] = dis[dx][dy] + p->time;
		front_point[des_x][des_y].first = dx;
		front_point[des_x][des_y].second = dy;
		route_id[des_x][des_y] = p->id;
		q.push(make_pair(-dis[des_x][des_y], make_pair(des_x, des_y)));
	}

需求2

需求2求解的問題是最少車票花費問題,車票的花費主要受兩個因素的影響:
1、單張車票的價格
2、換乘次數,換乘時要花費新的車票費用。
因爲求解的問題是最少花費問題,可以顯而易見的是,當某乘客乘坐i號線路並換乘到j號線路後,她不會再上i號線路,乘坐過程中也不會重複經過某個點,因爲這兩種決策都會導致時間的浪費。因此可以規定Dij過程中維持的原則:每個點不被重複經過且每個線路只能走一次。
關於算法的流程與上述相同,這裏不再贅述,值得展開的是鬆弛
鬆弛原則
如果源點到該邊起點花費+該邊花費<源點到該邊終點花費,進行鬆弛
但是與需求1不同的是·,這裏的邊花費不再是可以直接訪問邊信息得到,而是需要進行一定的處理:
首先在記性Dij的過程中,定義一個變量存儲當前走的線路:

int last_id = route_id[dx][dy];

在進行鬆弛之前,訪問到邊的id與當前線路id做比較:
如果相同說明更新的線路和正在乘坐的線路id相同,不需要花費票價就可以繼續乘坐。
如果不相同說明鬆弛這個點需要進行換乘,要花費新的票價乘坐。
利用這樣的數據處理,就可以將較複雜的花費問題轉換成基本的Dij操作,從而輕鬆解決。
鬆弛操作
1、將花費數組更新
2、將終點加入小根堆
3、更新終點的前置節點數組
4、更新終點的前置路線id數組

if (last_id == p->id)
new_price = 0;
else
new_price = p->cost;
if (price[des_x][des_y] > price[dx][dy] + new_price)
{
price[des_x][des_y] = price[dx][dy] + new_price;
front_point[des_x][des_y].first = dx;
front_point[des_x][des_y].second = dy;
route_id[des_x][des_y] = p->id;
q.push(make_pair(-price[des_x][des_y], make_pair(des_x, des_y)));
}

需求3

需求3在需求1的基礎上,考慮等車時間,並選出最省時的路徑。
雖然題目給定了發車間隔,但是在使用者使用該程序時,默認整個公交系統從起始態開始運行,每輛車需要不同的時間到每個站點,因此在求解的同時,我們需要模擬公交系統的形式狀態,這樣才能給出真實的等待時間。

需求3核心算法分析

分析公交的運行方式,可以總結使用者在某站等車時有兩種情況:
1、第一班車還沒到,需要等待時間=始發站到該站的時間—使用者到該站的時間
2、第一版車已經過去,需要等待的時間=(使用者到該站的時間–始發站到該站的時間)%線路發車間隔
上述計算時間所用到的變量有三個始發站到該站的時間使用者到該站的時間線路發車間隔,其中線路發車間隔在設計ADT中我們將其存放在了map中可以隨時查找;使用者到該站的時間作爲該算法的鬆弛指標dis[x][y];需要特殊處理的是始發站到該站的時間
對於該變量,我和隊友在設計時有兩種設計思想:
1、不存儲始發站到該站的時間,而是利用使用者到該站的時間,每次使用時直接動態計算出需要等待的時間
2、在初始化線路時,將始發站到該站的時間提前計算好並且存放下來,在後續使用時直接查找並套入上述公式計算等待的時間。
兩種算法代表了兩種思想:
算法1使用時間換空間,不需要爲線路的運行時間分配空間,但由於每次調用需要計算,存在大量的重複計算,時間性能差但是佔用空間小;
算法2使用空間換取時間,存儲下線路各站點的運行時間,在每次鬆弛操作前套用公式即可,時間性能優秀但佔用空間。
經過線路佔用空間比較和大數據集對動態計算的影響,進行權衡過後,我們得出了這樣的結論:存儲站點行駛時間佔用的空間遠小於存放線路圖佔用的空間,空間耗費幾乎可以忽略不計。但是在大數據集下,算法2對比算法1擁有明顯的時間性能優勢。因此採取算法2,使用存儲信息–>套公式的方法來計算得到需要等待的時間。
鬆弛原則
這裏要注意的是,上述的算法描述都基於換乘等待時間。需求3的實現基於需求1和需求2做了結合,在考慮行駛時間的同時,也要考慮線路問題,如果待更新邊的線路號與當前行駛線路號相同,等待時間爲0;只有在線路號不同要進行換乘時,需要使用上述的算法進行求解。
可以將鬆弛的準備操作描述如下:
1、判斷待更新邊的路線id與當前行駛id的關係,如果相等則等待時間爲零,直接向下完成鬆弛
2、如果不相等,需要換乘操作,訪問存儲查找數據並套用公式,計算出等待時間。
如果源點到該邊起點時間+該邊行駛時間+換乘等待時間<源點到該邊終點時間,進行鬆弛
鬆弛操作:
1、將時間數組更新
2、將終點加入小根堆
3、更新終點的前置節點數組
4、更新終點的前置路線id數組
5、更新當前行駛的id號

if (des_direct == 0)//邊是正向
{
	{
		need_time = point_map[dx][dy].record[des_id].first;
	}
	else if (des_direct == 1)//邊是負向
	{
		need_time = point_map[dx][dy].record[des_id].second;
	}
	if (dis[dx][dy] >= need_time)//到這個點的時候,第一班車已經走了
	{
		wait_time = (dis[dx][dy] - need_time) % wait_map[des_id];
	}
	else//到這個點的時候,車還沒到
	{
		wait_time = need_time - dis[dx][dy];
	}
}
if (dis[des_x][des_y] > dis[dx][dy] + des_time + wait_time)
{
	dis[des_x][des_y] = dis[dx][dy] + des_time + wait_time;
	front_point[des_x][des_y].first = dx;
	front_point[des_x][des_y].second = dy;
	route_id[des_x][des_y] = p->id;
	q.push(make_pair(-dis[des_x][des_y], make_pair(des_x, des_y)));
}

實現源代碼(C++)

#include<iostream>
#include<stdio.h>
#include<map>
#include<queue>
#include<algorithm>
#include<vector>
#include<math.h>
using namespace std;
const int max_i = 1e8;

struct edge
{
	pair<int, int> begin_point;//起點
	pair<int, int> des_point;//終點
	int id;
	int time;
	int cost;
	int direct;
	edge* next;//指向下一個

	edge()
	{
		this->begin_point.first = 0;
		this->begin_point.second = 0;
		this->des_point.first = 0;
		this->des_point.second = 0;
		this->id = 0;
		this->time = 0;
		this->cost=0;
		this->direct = -1;
		this->next = NULL;
	}
	void initialize(int begin_x, int begin_y, int des_x, int des_y, int id, int value, int speed,int dir)
	{
		begin_point.first = begin_x;
		begin_point.second = begin_y;
		des_point.first = des_x;
		des_point.second = des_y;
		double a = (begin_x - des_x) * (begin_x - des_x) + (begin_y - des_y) * (begin_y - des_y);
		int route = (int)sqrt(a);
		int route_time = (route / speed) + 1;
		this->id = id;
		this->time = route_time;
		this->cost = value;
		this->direct = dir;
	}
};
struct point_list
{
	map<int, pair<int, int>> record;
	point_list()
	{
		record.clear();
	}
	void insert_time(int id, int plus_time, int minus_time)
	{
		record[id] = make_pair(plus_time, minus_time);
	}
};

struct y_list//標識每個定點y座標的結構體
{
	edge* first_edge;
};

struct x_list
{
	y_list* vertex;
};

class Graph
{
private:
	int vertex_num;//出發點的個數
	int route_num;//路徑的條數
	x_list* graph_p;//存圖
	map<int, int> wait_map;//存放每趟路線的發車間隔
	point_list** point_map;//存放每個點的到每一個始發站和終點站的時間
public:
	Graph(int graph_num)
	{
		vertex_num = graph_num;
		route_num = 0;
		graph_p = new x_list[vertex_num + 1];
		point_map = new point_list * [vertex_num + 1];
		for (int i = 0; i <= vertex_num; i++)
		{
			graph_p[i].vertex = new y_list[vertex_num + 1];
			point_map[i] = new point_list[vertex_num + 1];
			for (int j = 0; j <= vertex_num; j++)
			{
				graph_p[i].vertex[j].first_edge = NULL;
			}
		}
		wait_map.clear();
	}
	void add_line(vector<pair<int, int>>* station, int speed, int value,int wait_time,int id)
	{
		route_num++;
		wait_map[id] = wait_time;
		for (int i = 0; i < (station->size() - 1); i++)
		{
			int x1 = station->at(i).first;
			int y1 = station->at(i).second;
			int x2 = station->at(i + 1).first;
			int y2 = station->at(i + 1).second;
			edge * plist = graph_p[x1].vertex[y1].first_edge;//指向頭結點
			edge* new_node = new edge();
			new_node->initialize(x1, y1, x2, y2, id, value, speed,0);//車正向行駛的邊標號爲0
			new_node->next = plist;
			graph_p[x1].vertex[y1].first_edge = new_node;
			
		}
		for (int i = station->size() - 1; i >= 1; i--)
		{
			int x1 = station->at(i).first;
			int y1 = station->at(i).second;
			int x2 = station->at(i - 1).first;
			int y2 = station->at(i - 1).second;
			edge * plist = graph_p[x1].vertex[y1].first_edge;//指向頭結點
			edge* new_node = new edge();
			new_node->initialize(x1, y1, x2, y2, id, value, speed,1);//車逆向行駛的邊標號爲1
			new_node->next = plist;
			graph_p[x1].vertex[y1].first_edge = new_node;
		}
		int start_x = station->at(0).first;
		int start_y = station->at(0).second;
		int end_x = station->at(station->size() - 1).first;
		int end_y = station->at(station->size() - 1).second;
		double a = (start_x - end_x) * (start_x - end_x) + (start_y - end_y) * (start_y - end_y);
		int route_dis = (int)sqrt(a);
		int route_time = (route_dis / speed) + 1;
		point_map[start_x][start_y].insert_time(id, 0, route_time);
		point_map[end_x][end_y].insert_time(id, route_time, 0);
		int route_time_plus = 0;
		int route_time_minus = 0;
		for (int i = 1; i < station->size() - 1; i++)
		{
			int dx = station->at(i).first;
			int dy = station->at(i).second;
			a = (dx - start_x) * (dx - start_x) + (dy - start_y) * (dy - start_y);
			route_dis = (int)sqrt(a);
			route_time_plus = (route_dis / speed) + 1;
			a = (dx - end_x)*(dx - end_x) + (dy - end_y)*(dy - end_y);
			route_dis = (int)sqrt(a);
			route_time_minus = (route_dis / speed) + 1;
			point_map[dx][dy].insert_time(id, route_time_plus, route_time_minus);
		}
	}
	void output()
	{
		for (int i = 0; i <= vertex_num; i++)
		{
			for (int j = 0; j <= vertex_num; j++)
			{
				cout << "(" << i << "," << j << ")" << " ";
				edge* p = graph_p[i].vertex[j].first_edge;
				while (p != NULL)
				{
					cout << "(" << p->begin_point.first << "," << p->begin_point.second << ")" << "-->" << "(" << p->des_point.first << "," << p->des_point.second << ")" << ": ";
					cout << " id:" << p->id << " cost:" << p->cost << " time: " << p->time<<"direct:"<<p->direct;
					p = p->next;
				}
				cout << endl;
			}
		}
	}
	void timemap_output()
	{
		for (int i = 0; i <= vertex_num; i++)
		{
			for (int j = 0; j <= vertex_num; j++)
			{
				cout << "(" << i << "," << j << ")" << " ";
				map<int, pair<int, int>>::iterator it;
				for (it = point_map[i][j].record.begin(); it != point_map[i][j].record.end(); it++)
				{
					cout <<"id="<< it->first << "正向時間:" << it->second.first << "負向時間:" << it->second.second<<" ";
				}
				cout << endl;
			}
		}
	}
	void Dijkstra_min_price(int x, int y, int res_x, int res_y)
	{
		//開闢空間
		int** price = NULL;//距離矩陣
		int** vis = NULL;//訪問矩陣
		int** route_id = NULL;//路線id的前置矩陣
		pair<int, int>** front_point = NULL;//前置節點的訪問矩陣
		price = new int* [vertex_num + 1];
		vis = new int* [vertex_num + 1];
		route_id = new int* [vertex_num + 1];
		front_point = new pair<int, int>* [vertex_num + 1];
		for (int i = 0; i <= vertex_num; i++)
		{
			price[i] = new int[vertex_num + 1];
			vis[i] = new int[vertex_num + 1];
			route_id[i] = new int[vertex_num + 1];
			front_point[i] = new pair<int, int>[vertex_num + 1];
		}
		//初始化
		for (int i = 0; i <= vertex_num; i++)
		{
			for (int j = 0; j <= vertex_num; j++)
			{
				price[i][j] = max_i;
				vis[i][j] = 0;
				route_id[i][j] = 0;
				front_point[i][j].first = 0;
				front_point[i][j].second = 0;
			}
		}
		priority_queue<pair<int, pair<int, int>>> q;
		while (!q.empty()) q.pop();
		price[x][y] = 0;
		route_id[x][y] = 0;
		q.push(make_pair(0, make_pair(x, y)));

		while (!q.empty())
		{
			int dx = q.top().second.first;
			int dy = q.top().second.second;
			int last_id = route_id[dx][dy];
			q.pop();

			if (vis[dx][dy] == 1) continue;
			vis[dx][dy] = 1;

			edge* p = graph_p[dx].vertex[dy].first_edge;
			while (p != NULL)
			{
				int des_x = p->des_point.first;
				int des_y = p->des_point.second;
				int new_price = 0;
				if (last_id == p->id)
					new_price = 0;
				else
					new_price = p->cost;
				if (price[des_x][des_y] > price[dx][dy] + new_price)
				{
					price[des_x][des_y] = price[dx][dy] + new_price;
					front_point[des_x][des_y].first = dx;
					front_point[des_x][des_y].second = dy;
					route_id[des_x][des_y] = p->id;
					q.push(make_pair(-price[des_x][des_y], make_pair(des_x, des_y)));
				}
				p = p->next;
			}
		}
		if (price[res_x][res_y] == max_i)
			cout << "該車站不可達" << endl;
		else
			printf("(%d,%d)到(%d,%d)的最小票價是%d\n", x, y, res_x, res_y, price[res_x][res_y]);
		vector<pair<int, int>> vec;
		int flag1 = res_x;
		int flag2 = res_y;
		int front_flag1 = res_x;
		int front_flag2 = res_y;
		while (flag1 != x || flag2 != y)
		{
			vec.push_back(make_pair(flag1, flag2));
			front_flag1 = front_point[flag1][flag2].first;
			front_flag2 = front_point[flag1][flag2].second;
			flag1 = front_flag1;
			flag2 = front_flag2;
		}
		printf("起始點(%d,%d)\n", x, y);
		int dx2 = vec.at(vec.size() - 1).first;
		int dy2 = vec.at(vec.size() - 1).second;
		int now_id = route_id[dx2][dy2];
		printf("線路%d:", now_id);
		printf("(%d,%d) ", x, y);
		int last_x = x;
		int last_y = y;
		for (int i = vec.size() - 1; i >= 0; i--)
		{
			if (now_id == route_id[vec.at(i).first][vec.at(i).second])
				printf("(%d,%d) ", vec.at(i).first, vec.at(i).second);
			else
			{
				now_id = route_id[vec.at(i).first][vec.at(i).second];
				printf("\n在(%d,%d)換乘到線路%d:(%d,%d) ", last_x, last_y, now_id, last_x, last_y);
				printf("(%d,%d) ", vec.at(i).first, vec.at(i).second);
			}
			last_x = vec.at(i).first;
			last_y = vec.at(i).second;
		}
		printf("\n終結點(%d,%d)\n", res_x, res_y);
	}
	void Dijkstra_min_time(int x, int y, int res_x, int res_y)
	{
		//開闢空間
		int** dis = NULL;//距離矩陣
		int** vis = NULL;//訪問矩陣
		int** route_id = NULL;//路線id的前置矩陣
		pair<int, int>** front_point = NULL;//前置節點的訪問矩陣
		dis = new int* [vertex_num + 1];
		vis = new int* [vertex_num + 1];
		route_id = new int* [vertex_num + 1];
		front_point = new pair<int, int>* [vertex_num + 1];
		for (int i = 0; i <= vertex_num; i++)
		{
			dis[i] = new int[vertex_num + 1];
			vis[i] = new int[vertex_num + 1];
			route_id[i] = new int[vertex_num + 1];
			front_point[i] = new pair<int, int>[vertex_num + 1];
		}
		//初始化
		for (int i = 0; i <= vertex_num; i++)
		{
			for (int j = 0; j <= vertex_num; j++)
			{
				dis[i][j] = max_i;
				vis[i][j] = 0;
				route_id[i][j] = 0;
				front_point[i][j].first = 0;
				front_point[i][j].second = 0;
			}
		}
		priority_queue<pair<int, pair<int, int>>> q;
		while (!q.empty()) q.pop();
		dis[x][y] = 0;
		route_id[x][y] = 0;
		q.push(make_pair(0, make_pair(x, y)));

		while (!q.empty())
		{
			int dx = q.top().second.first;
			int dy = q.top().second.second;
			q.pop();

			if (vis[dx][dy] == 1) continue;
			vis[dx][dy] = 1;

			edge * p = graph_p[dx].vertex[dy].first_edge;
			while (p != NULL)
			{
				int des_x = p->des_point.first;
				int des_y = p->des_point.second;
				if (dis[des_x][des_y] > dis[dx][dy] + p->time)
				{
					dis[des_x][des_y] = dis[dx][dy] + p->time;
					front_point[des_x][des_y].first = dx;
					front_point[des_x][des_y].second = dy;
					route_id[des_x][des_y] = p->id;
					q.push(make_pair(-dis[des_x][des_y], make_pair(des_x, des_y)));
				}
				p = p->next;
			}
		}
		if (dis[res_x][res_y] == max_i)
			cout << "該車站不可達" << endl;
		else
			printf("(%d,%d)到(%d,%d)的最短時間是%d\n", x, y, res_x, res_y, dis[res_x][res_y]);
		vector<pair<int, int>> vec;
		int flag1 = res_x;
		int flag2 = res_y;
		int front_flag1 = res_x;
		int front_flag2 = res_y;
		while (flag1 != x || flag2 != y)
		{
			vec.push_back(make_pair(flag1, flag2));
			front_flag1 = front_point[flag1][flag2].first;
			front_flag2 = front_point[flag1][flag2].second;
			flag1 = front_flag1;
			flag2 = front_flag2;
		}
		printf("起始點(%d,%d)\n", x, y);
		int dx2 = vec.at(vec.size() - 1).first;
		int dy2 = vec.at(vec.size() - 1).second;
		int now_id = route_id[dx2][dy2];
		printf("線路%d:", now_id);
		printf("(%d,%d) ", x, y);
		int last_x = x;
		int last_y = y;
		for (int i = vec.size() - 1; i >= 0; i--)
		{
			if (now_id == route_id[vec.at(i).first][vec.at(i).second])
				printf("(%d,%d) ", vec.at(i).first, vec.at(i).second);
			else
			{
				now_id = route_id[vec.at(i).first][vec.at(i).second];
				printf("\n在(%d,%d)換乘到線路%d:(%d,%d) ",last_x,last_y, now_id,last_x,last_y);
				printf("(%d,%d) ", vec.at(i).first, vec.at(i).second);
			}
			last_x = vec.at(i).first;
			last_y = vec.at(i).second;
		}
		printf("\n終結點(%d,%d)\n", res_x, res_y);
	}
	void Dijkstra_min_time_wait(int x, int y, int res_x, int res_y)
	{
		//開闢空間
		int** dis = NULL;//時間矩陣
		int** vis = NULL;//訪問矩陣
		int** route_id = NULL;//路線id的前置矩陣
		pair<int, int>** front_point = NULL;//前置節點的訪問矩陣
		dis = new int* [vertex_num + 1];
		vis = new int* [vertex_num + 1];
		route_id = new int* [vertex_num + 1];
		front_point = new pair<int, int>* [vertex_num + 1];
		for (int i = 0; i <= vertex_num; i++)
		{
			dis[i] = new int[vertex_num + 1];
			vis[i] = new int[vertex_num + 1];
			route_id[i] = new int[vertex_num + 1];

			front_point[i] = new pair<int, int>[vertex_num + 1];
		}
		//初始化
		for (int i = 0; i <= vertex_num; i++)
		{
			for (int j = 0; j <= vertex_num; j++)
			{
				dis[i][j] = max_i;
				vis[i][j] = 0;
				route_id[i][j] = 0;
				front_point[i][j].first = 0;
				front_point[i][j].second = 0;
			}
		}
		priority_queue<pair<int, pair<int, int>>> q;
		while (!q.empty()) q.pop();
		dis[x][y] = 0;
		route_id[x][y] = 0;
		q.push(make_pair(0, make_pair(x, y)));

		while (!q.empty())
		{
			int dx = q.top().second.first;
			int dy = q.top().second.second;
			int last_id = route_id[dx][dy];
			q.pop();

			if (vis[dx][dy] == 1) continue;
			vis[dx][dy] = 1;

			edge * p = graph_p[dx].vertex[dy].first_edge;
			while (p != NULL)
			{
				int des_x = p->des_point.first;
				int des_y = p->des_point.second;
				int des_time = p->time;//走邊的時間
				int des_id = p->id;
				int des_direct = p->direct;
				int wait_time = 0;//等待時間
				int need_time = 0;//從始發站到該點用的時間

				if (des_id == last_id)//如果是同一條路線上的,不需要等車
				{
					wait_time = 0;
				}
				else
				{
					if (des_direct == 0)//邊是正向
					{
						need_time = point_map[dx][dy].record[des_id].first;
					}
					else if (des_direct == 1)//邊是負向
					{
						need_time = point_map[dx][dy].record[des_id].second;
					}
					if (dis[dx][dy] >= need_time)//到這個點的時候,第一班車已經走了
					{
						wait_time = (dis[dx][dy] - need_time) % wait_map[des_id];
					}
					else//到這個點的時候,車還沒到
					{
						wait_time = need_time - dis[dx][dy];
					}
				}
				if (dis[des_x][des_y] > dis[dx][dy] + des_time + wait_time)
				{
					dis[des_x][des_y] = dis[dx][dy] + des_time + wait_time;
					front_point[des_x][des_y].first = dx;
					front_point[des_x][des_y].second = dy;
					route_id[des_x][des_y] = p->id;
					q.push(make_pair(-dis[des_x][des_y], make_pair(des_x, des_y)));
				}
				p = p->next;
			}
		}
		if (dis[res_x][res_y] == max_i)
			cout << "該車站不可達" << endl;
		else
			printf("(%d,%d)到(%d,%d)的最短時間是%d\n", x, y, res_x, res_y, dis[res_x][res_y]);
		vector<pair<int, int>> vec;
		int flag1 = res_x;
		int flag2 = res_y;
		int front_flag1 = res_x;
		int front_flag2 = res_y;
		while (flag1 != x || flag2 != y)
		{
			vec.push_back(make_pair(flag1, flag2));
			front_flag1 = front_point[flag1][flag2].first;
			front_flag2 = front_point[flag1][flag2].second;
			flag1 = front_flag1;
			flag2 = front_flag2;
		}
		printf("起始點(%d,%d)\n", x, y);
		int dx2 = vec.at(vec.size() - 1).first;
		int dy2 = vec.at(vec.size() - 1).second;
		int now_id = route_id[dx2][dy2];
		printf("線路%d:", now_id);
		printf("(%d,%d) ", x, y);
		int last_x = x;
		int last_y = y;
		for (int i = vec.size() - 1; i >= 0; i--)
		{
			if (now_id == route_id[vec.at(i).first][vec.at(i).second])
				printf("(%d,%d) ", vec.at(i).first, vec.at(i).second);
			else
			{
				now_id = route_id[vec.at(i).first][vec.at(i).second];
				printf("\n在(%d,%d)換乘到線路%d:(%d,%d) ", last_x, last_y, now_id, last_x, last_y);
				printf("(%d,%d) ", vec.at(i).first, vec.at(i).second);
			}
			last_x = vec.at(i).first;
			last_y = vec.at(i).second;
		}
		printf("\n終結點(%d,%d)\n", res_x, res_y);
	}
};
int main()
{
	vector<pair<int, int>>* route_station;
	route_station = new vector<pair<int, int>>;
	int point_num;//點範圍
	cin >> point_num;
	Graph a(point_num);
	int line_num;//路線數
	cin >> line_num;
	for (int i = 0; i < line_num; i++)
	{
		int station_num = 0;//站點數量
		cin >> station_num;
		int x, y;
		route_station->clear();
		for (int i = 0; i < station_num; i++)
		{
			cin >> x;
			cin >> y;
			route_station->push_back(make_pair(x, y));
		}
		int cin_value;
		int cin_speed;
		int wait_time;
		int cin_id;
		cout << "id:";
		cin >> cin_id;
		cout << "value:";
		cin >> cin_value;
		cout << "speed:";
		cin >> cin_speed;
		cout << "wait time:";
		cin >> wait_time;
		a.add_line(route_station, cin_value, cin_speed,wait_time,cin_id);
	}
	//a.output();
	//a.timemap_output();
	cout << "最快時間模式(不考慮等車)" << endl;
	cout << "輸入起點" << endl;
	int begin_x1, begin_y1, desti_x1, desti_y1;
	cin >> begin_x1 >> begin_y1;
	cout << "輸入終點"<<endl;
	cin >> desti_x1 >> desti_y1;
	a.Dijkstra_min_time(begin_x1, begin_y1, desti_x1, desti_y1);
	cout << "最快時間模式(考慮等車)" << endl;
	cout << "輸入起點" << endl;
	int begin_x3, begin_y3, desti_x3, desti_y3;
	cin >> begin_x3 >> begin_y3;
	cout << "輸入終點" << endl;
	cin >> desti_x3 >> desti_y3;
	a.Dijkstra_min_time_wait(begin_x3, begin_y3, desti_x3, desti_y3);
	cout << "最小花費模式" << endl;
	cout << "輸入起點" << endl;
	int begin_x2, begin_y2, desti_x2, desti_y2;
	cin >> begin_x2 >> begin_y2;
	cout << "輸入終點" << endl;
	cin >> desti_x2 >> desti_y2;
	a.Dijkstra_min_price(begin_x2, begin_y2, desti_x2, desti_y2);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章