1. 程式人生 > >中國大學MOOC-陳越、何欽銘-資料結構-2018秋——關鍵活動

中國大學MOOC-陳越、何欽銘-資料結構-2018秋——關鍵活動

我的中國大學MOOC-陳越、何欽銘-資料結構-2018秋程式碼倉:https://github.com/617076674/MOOC-DataStructure-2018-Autumn

題目描述:

知識點:關鍵路徑

思路:對邊取相反數,用SPFA演算法求關鍵路徑

題目要求的是所有關鍵活動。也就是說,如果有兩條長度相同關鍵路徑,其上面的活動都應該被羅列出來,但不能羅列相同的路徑。活動的羅列順序不是活動進行的順序,而是根據題目的最後一句話——“任務開始的交接點編號小者優先,起點編號相同時,與輸入時任務的順序相反”。

為了快速定位該活動的輸入順序,我們用一個二維陣列inputSequence[][]儲存各邊對應的輸入順序

。inputSequence[i][j]表示從i到j的有向邊的輸入順序。

為了找到起點,我們還需要統計每一個點的入度,其中入度為0的點才可能是起點。

對所有入度為0的點,我們都以其為起點做SPFA演算法,取其到其他點的最小距離,如果該最小距離比當前記錄的最小距離還要小,我們就需要清空儲存結果邊資訊的resultActivities,將當前路徑所經過的邊加入resultActivities。如果該最小距離與當前記錄的最小距離相等,我們不需要清空resultActivities,直接把當前路徑所經過的邊加入resultActivities即可。

dfs尋找當前路徑所經過的邊時,為了防止向resultActivities重複新增邊

。我們還需要一個visited[]陣列來標記某條邊是否已被加入resultActivities,visited[i] == true,說明輸入順序為i的那條邊已經被加入了resultActivities,visited[i] == false,說明輸入順序為i的那條邊還沒有加入resultActivities。

時間複雜度主要來源於多次SPFA演算法,一次SPFA演算法的時間複雜度是O(kM)。空間複雜度是O(N ^ 2)。

C++程式碼:

#include<iostream>
#include<vector>
#include<queue>
#include<set>
#include<algorithm>

using namespace std;

struct node {
	int v;
	int len;
	node(int _v, int _len) {
		v = _v;
		len = _len;
	}
};

struct activity {
	int startV, endV;
	activity(int _startV, int _endV) {
		startV = _startV;
		endV = _endV;
	}
};

int N, M, inDegree[101], d[101], INF = 1000000000, countInq[101], resultMinTime = INF, startPoint;
vector<node> graph[101];	//鄰接表形式儲存圖 
bool inq[101];
vector<int> tempPath;
set<int> pre[101];
int inputSequence[101][101];	//記錄每條邊輸入時的順序
vector<activity> resultActivities;	//儲存關鍵路徑上的邊資訊 

void init();
bool spfa(int s);
void dfs(int nowVisit, bool visited[]);
bool cmp(activity a1, activity a2);

int main() {
	scanf("%d %d", &N, &M);	//讀取節點數和邊數 
	bool visited[M];	//標記第i條邊是否已經在結果集裡 
	fill(inDegree + 1, inDegree + N + 1, 0);
	int v1, v2, len;
	for(int i = 0; i < M; i++) {
		scanf("%d %d %d", &v1, &v2, &len);
		graph[v1].push_back(node(v2, -len));
		inDegree[v2]++;
		inputSequence[v1][v2] = i;
	}
	for(int i = 1; i <= N; i++) {
		if(inDegree[i] == 0) {	//入度為0的點是起點
			init();
			startPoint = i;	//以點i為起點
			bool flag = spfa(startPoint);	//計算點i到其餘點的最短路徑
			if(flag) {
				int minTime = INF;
				for(int j = 1; j <= N; j++) {
					if(j != i && d[j] < minTime) {	//尋找從點i到其餘點的最短路徑中的最小值
						minTime = d[j];	//更新最小時間資訊
					}
				}
				if(minTime < resultMinTime) {	//如果以i為起點到其餘點的最短路徑的最小值比當前的最小路徑還要小
					resultMinTime = minTime;	//更新最小路徑資訊
					resultActivities.clear();	//清空resultActivities
					fill(visited, visited + M, false);	//標記所有節點未被訪問 
					for(int j = 1; j <= N; j++) {
						if(j != i && d[j] == minTime) {
							dfs(j, visited);
						}
					}
				} else if(minTime == resultMinTime) {
					for(int j = 1; j <= N; j++) {
						if(j != i && d[j] == minTime) {
							dfs(j, visited);
						}
					}
				}
			}
		}
	}
	if(resultMinTime == INF) {
		printf("0\n");
		return 0;
	}
	printf("%d\n", -resultMinTime);	//輸出關鍵路徑長度 
	sort(resultActivities.begin(), resultActivities.end(), cmp);	//對結果邊進行排序 
	for(int i = 0; i < resultActivities.size(); i++) {	//輸出結果 
		printf("%d->%d\n", resultActivities[i].startV, resultActivities[i].endV);
	}
	return 0;
}

void init() {
	fill(d + 1, d + N + 1, INF);
	fill(inq + 1, inq + N + 1, false);
	fill(countInq + 1, countInq + N + 1, 0);
	for(int j = 1; j <= N; j++) {
		pre[j].clear();
	}
}

bool spfa(int s) {
	d[s] = 0;
	queue<int> q;
	q.push(s);
	inq[s] = true;
	countInq[s]++;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		inq[u] = false;
		for(int i = 0; i < graph[u].size(); i++) {
			int v = graph[u][i].v;
			int len = graph[u][i].len;
			if(d[u] + len < d[v]) {
				d[v] = d[u] + len;
				pre[v].clear();
				pre[v].insert(u);
				if(!inq[v]) {
					q.push(v);
					inq[v] = true;
					countInq[v]++;
					if(countInq[v] > N - 1) {
						return false;
					}
				}
			} else if(d[u] + len == d[v]) {
				pre[v].insert(u);
				if(!inq[v]) {
					q.push(v);
					inq[v] = true;
					countInq[v]++;
					if(countInq[v] > N - 1) {
						return false;
					}
				}
			}
		}
	}
	return true;
}

void dfs(int nowVisit, bool visited[]) {
	tempPath.push_back(nowVisit);
	if(nowVisit == startPoint) {
		for(int i = tempPath.size() - 1; i > 0; i--) {
			int inputNumber = inputSequence[tempPath[i]][tempPath[i - 1]];
			if(!visited[inputNumber]) {
				resultActivities.push_back(activity(tempPath[i], tempPath[i - 1]));
				visited[inputNumber] = true;
			}
		}
		tempPath.pop_back();
		return;
	}
	for(set<int>::iterator it = pre[nowVisit].begin(); it != pre[nowVisit].end(); it++) {
		dfs(*it, visited);
	}
	tempPath.pop_back();
}

bool cmp(activity a1, activity a2) {
	if(a1.startV == a2.startV) {
		return inputSequence[a1.startV][a1.endV] > inputSequence[a2.startV][a2.endV];
	} else {
		return a1.startV < a2.startV;
	}
}

C++解題報告: