記PAT A1003題解題過程

在這裏插入圖片描述

這是題目,給各位翻譯一下。現有一個城市羣。每個城市都有自己的救援力量,每個城市之間的路途距離也有給出。現在你作爲區域的救援隊長,一旦出現緊急情況。你需要在最短的距離趕到救援現場,並儘可能從途中帶走更多的救援力量到達救災點。輸入的第一行分別是:城市數(頂點數),城市之間的道路數(邊數),救援隊長出發點,事故發生點,第二行是每個城市的救援力量,接下來的邊數行是邊權值信息。每行對應着起點,終點,以及路途距離。輸出是最短路徑的數目和可以攜帶到的最大救援力量。
(我的解釋喜歡寫在代碼裏。算作註釋。)

#include <iostream>
using namespace std;

const int MAXV = 1000;
const int INF = 1000000000;
int g[MAXV][MAXV];  //圖的邊值。未初始化的全局變量會自動初始化爲0
bool vit[MAXV] = { false };   //判斷是否已經訪問過。
int d[MAXV];   //記錄最短距離。
//這是常規的三個數組

int num[MAXV];   //記錄到達這個頂點的最短路徑的數量,默認初始化起點的值爲1,其餘爲0
int weight[MAXV];    //記錄該頂點的救援力量。主函數一開始就會由輸入初始化。
int w[MAXV];   //記錄到達該頂點的最大救援力量。

void Dijkstra(int n) {
	for (int i = 0; i < n; i++) {
		int u = -1; int MIN = INF;
		for (int j = 0; j < n; j++) {
			if (vit[j] == false && d[j] < MIN) {
				u = j;
				MIN = d[j];
			}
		}
		if (u == -1) return;
		vit[u] = true;
		for (int v = 0; v < n; v++) {
			if (vit[v] == false && g[u][v] != 0) {
				if (d[u] + g[u][v] < d[v]) {
					d[v] = d[u] + g[u][v];
					//最小距離改進。還需要維護num數組和w數組。
					num[v] = num[u];   //如果走u過,距離可以更短,那麼v的數量就和u的數量相等。
					w[v] = w[u] + weight[v];   //如果距離更近,直接選擇這條路,不考慮稍微遠的那條路是否有更大的救援力量。
					                           //因爲題意要求的就是儘快到達,之後再有儘可能多的救援力量。
				}
				else if(d[u]+g[u][v]==d[v]){
					num[v] += num[u];  //如果一樣遠,那麼v的數量就需要加上u的數量。
					if (w[u] + weight[v] > w[v]) {
						w[v] = w[u] + weight[v];
					}
				}
			}
		}
	}
}

int main() {
	int N, M;
	int C1, C2;
	fill(d, d + MAXV, INF);
	cin >> N >> M >> C1 >> C2;
	num[C1] = 1;
	d[C1] = 0;
	for (int i = 0; i < N; i++) {
		cin>>weight[i];
	}
	w[C1] = weight[C1];
	for (int i = 0; i < M; i++) {
		int c, d1, e;
		cin >> c >> d1 >> e;
		g[c][d1] = g[d1][c] = e;
	}
	Dijkstra(N);
	cout << num[C2] << " "<<w[C2];
	return 0;
}

算法筆記還給出了更加模板化的寫法。就是先用Dijkstra算法記錄所有的最短路徑(只考慮距離),然後再從這些路徑裏面選擇出滿足第二尺度的最優的路徑。(第二尺度就是指點權啊,比如這裏的救援力量,邊權啊,比如走一條邊的花費等等。)這裏給出的方法是用DFS算法去找出滿足第二尺度最優的路線。

#include <iostream>
#include <vector>
using namespace std;

const int MAXV = 1000;
const int INF = 1000000000;
int g[MAXV][MAXV];  //圖的邊值。
bool vit[MAXV] = { false };   //判斷是否已經訪問過。
int d[MAXV];   //記錄最短距離。
//這是常規的三個數組

int num[MAXV];   //記錄到達這個頂點的最短路徑的數量,默認初始化起點的值爲1,其餘爲0
int weight[MAXV];    //記錄該頂點的救援力量。主函數一開始就會由輸入初始化。
vector<int> pre[MAXV];   //這是一個數組,每個數組元素都是一個數組。
int optvalue=0;
vector<int> path, temppath;  //記錄最短的那個路徑以及中間臨時的路徑。

void Dijkstra(int n) {
	for (int i = 0; i < n; i++) {
		int u = -1; int MIN = INF;
		for (int j = 0; j < n; j++) {
			if (vit[j] == false && d[j] < MIN) {
				u = j;
				MIN = d[j];
			}
		}
		if (u == -1) return;
		vit[u] = true;
		for (int v = 0; v < n; v++) {
			if (vit[v] == false && g[u][v] != 0) {
				if (d[u] + g[u][v] < d[v]) {
					d[v] = d[u] + g[u][v];
					//最小距離改進。
					pre[v].clear();
					pre[v].push_back(u);
					num[v] = num[u];   //如果走u過,距離可以更短,那麼v的數量就和u的數量相等。
				}
				else if (d[u] + g[u][v] == d[v]) {
					pre[v].push_back(u);
					num[v] += num[u];  //如果一樣遠,那麼v的數量就需要加上u的數量。
				}
			}
		}
	}
}
//重點是這裏加的這個DFS1函數,這裏來處理第二尺度。意思就是說把兩個尺度的處理分開,使之更加的清晰可控,邏輯沒那麼複雜。
void DFS1(int C1,int C2) {
	//先處理邊界
	if (C2 == C1) {
		temppath.push_back(C2);
		int value = 0;
		for (int i = 0; i < temppath.size(); i++) {
			value += weight[temppath[i]];
		}
		if (value > optvalue) {
			optvalue = value;
			path = temppath;
		}
		temppath.pop_back();
		return;
	}
	temppath.push_back(C2);
	for (int i = 0; i < pre[C2].size(); i++) {
		DFS1(C1, pre[C2][i]);
	}
	temppath.pop_back();
}

int main() {
	int N, M;
	int C1, C2;   //起點和終點
	fill(d, d + MAXV, INF);
	cin >> N >> M >> C1 >> C2;
	num[C1] = 1;
	d[C1] = 0;
	for (int i = 0; i < N; i++) {
		cin >> weight[i];
	}
	for (int i = 0; i < M; i++) {
		int c, d1, e;
		cin >> c >> d1 >> e;
		g[c][d1] = g[d1][c] = e;
	}
	Dijkstra(N);
	DFS1(C1, C2);
	cout << num[C2] << " "<<optvalue;
	return 0;
}

好了,以上就是這道題的代碼以及思想。謝謝閱讀!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章