PAT Dijkstra相關題目

如果對Dijkstra算法不太瞭解,參見我的另一篇博文:最短路徑之Dijkstra算法

1003 Emergency (25分)

題目鏈接

分析

常規的Dijkstra求解最短路徑的題,只不過添加了“第二標尺”——點權,並要求輸出不同最短路徑的數目。

AC代碼

#include<iostream>
#include<algorithm>
using namespace std;
const int maxv = 500;
const int inf = 1000000000;
int n, G[maxv][maxv], weight[maxv], w[maxv], d[maxv], num[maxv];
bool vis[maxv];
void Dijkstra(int s) {
	/*初始化*/
	fill(d, d + maxv, inf);
	d[s] = 0; //注意初始化d[s]
	w[s] = weight[s]; num[s] = 1; //注意w和num的初始化方式
	for (int i = 0; i < n; i++) {
		int u = -1, MIN = inf; //初始化
		for (int j = 0; j < n; j++) {
			if (vis[j] == false && d[j] < MIN) {
				u = j; MIN = d[j];
			}
		}
		if (u == -1) return;
		vis[u] = true; //當心漏掉
		for (int v = 0; v < n; v++) {
			if (vis[v] == false && G[u][v] != inf) {
				if (d[u] + G[u][v] < d[v]) {
					d[v] = d[u] + G[u][v];
					w[v] = weight[v] + w[u];
					num[v] = num[u]; //num[v]被覆蓋
				}
				else if (d[u] + G[u][v] == d[v]) {
					num[v] += num[u]; //與點權無關
					if (weight[v] + w[u] > w[v]) w[v] = weight[v] + w[u];
				}
			}
		}
	}
}
int main() {
	int m, s, v;
	cin >> n >> m >> s >> v;
	for (int i = 0; i < n; i++) {
		scanf("%d", &weight[i]);
	}
	//容易漏掉的地方,注意要把沒有通路的城市之間的距離設爲inf
	//注意初始化G爲inf,G爲二維數組,G[0]表示首元素地址步長爲sizeof(int)
	fill(G[0], G[0] + maxv * maxv, inf); 
	while (m--) {
		int c1, c2, len;
		scanf("%d%d%d", &c1, &c2, &len);
		G[c1][c2] = G[c2][c1] = len; //兩個頂點之間距離
	}
	Dijkstra(s);
	cout << num[v] <<" "<< w[v];
	return 0;
}

1030 Travel Plan (30分)

題目鏈接

分析

需要輸出最短路徑,加一個DFS即可。

AC代碼

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 500, inf = 1000000000;
int n, G[maxn][maxn], d[maxn], c[maxn], cost[maxn][maxn], pre[maxn];
bool vis[maxn];
void Dijkstra(int s) {
	fill(d, d + maxn, inf);
	for (int i = 0; i < n; i++) pre[i] = i;
	c[s] = 0; d[s] = 0;
	//不要vis[0]=true
	for (int i = 0; i < n; i++) {
		int u = -1, MIN = inf;
		for (int j = 0; j < n; j++) {
			if (vis[j] == false && d[j] < MIN) {
				u = j; MIN = d[j];
			}
		}
		if (u == -1) return;
		vis[u] = true;
		for (int v = 0; v < n; v++) {
			if (vis[v] == false && G[u][v] != inf) {
				if (d[u] + G[u][v] < d[v]) {
					d[v] = d[u] + G[u][v];
					c[v] = c[u] + cost[u][v];
					pre[v] = u; //更改前驅
				}
				else if (d[u] + G[u][v] == d[v] && c[u] + cost[u][v] < c[v]) {
					c[v] = c[u] + cost[u][v];
					pre[v] = u; //更改前驅
				}
			}
		}
	}
}
void dfs(int &s,int v) {
	if (v == s) {
		printf("%d ", v);
		return;
	}
	dfs(s,pre[v]);
	printf("%d ", v);
}
int main() {
	int m, s, des;
	cin >> n >> m >> s >> des;
	fill(G[0], G[0] + maxn * maxn, inf);
	while (m--) {
		int c1, c2, td, tc;
		scanf("%d%d%d%d", &c1, &c2, &td, &tc);
		G[c1][c2] = G[c2][c1] = td;
		cost[c1][c2] = cost[c2][c1] = tc;
	}
	Dijkstra(s);
	dfs(s, des);
	cout << d[des] << " " << c[des];
	return 0;	
}

1018 Public Bike Management (30分)

題目鏈接

題目大意

每個自行車車站的最大容量爲一個偶數cmax,如果一個車站裏面自行車的數量恰好爲cmax / 2,那麼稱處於完美狀態。如果一個車站容量是滿的或者空的,控制中心(處於結點0處)就會攜帶或者從路上收集一定數量的自行車前往該車站,一路上會讓所有的車站沿途都達到完美。現在給出cmax,車站的數量n,問題車站sp,m條邊,還有距離,求最短路徑。如果最短路徑有多個,求能帶的最少的自行車數目的那條。如果還是有很多條不同的路。那麼就找一個從車站帶回的自行車數目最少的。注意帶回的時候不對車站車的數量進行調整。

分析

由於本題的計算不滿足最優子結構,那麼只用Dijkstra算法是不能求解或者即使能求解也是很麻煩的。需要先求出所有的完整路徑,再從所有路徑中選擇帶出數量最少(相同則選擇帶出數量最多)。

易錯點

由於題目描述中沒有特別說明所有車站都需要達到完美狀態、帶回的時候不對車站中車的數量進行調整,所以對題目的理解和思考該問題的方式很可能造成錯誤的思路。我剛開始理解的就是隻需要對目標車站進行調整…於是就寫不對,DFS函數是這樣寫的…

void dfs(int v) {
	if (v == 0) {
		tempath.push_back(v);
		int temsum = 0;
		for (int i = tempath.size() - 2; i > 0; i--) {
			temsum += weight[tempath[i]] - full / 2;
		}
		switch (flag) {  //flag==true表示目的車站車的數量爲0
		case true:
			if (temsum < full / 2) {
				if (temsum > sum || sum >= full / 2) {
					sum = temsum;
					path = tempath;
				}
			}
			break;
		case false:
			if (temsum < sum) {
				sum = temsum;
				path = tempath;
			}
			break;
		}
		tempath.pop_back();
		return;
	}
	tempath.push_back(v);
	for (int i = 0; i < pre[v].size(); i++) {
		dfs(pre[v][i]);
	}
	tempath.pop_back();
}

AC代碼

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 501, inf=1000000000;
int full, n, des, m;
int weight[maxn], G[maxn][maxn], d[maxn];
bool vis[maxn];
vector<int> pre[maxn], path, tempath;
void Dijkstra() {
	fill(d, d + maxn, inf);
	d[0] = 0;
	for (int i = 0; i <= n; i++) {
		int u = -1, MIN = inf;
		for (int j = 0; j <= n; j++) {
			if (vis[j]==false&&d[j] < MIN) {
				u = j; MIN = d[j];
			}
		}
		if (u == -1) return;
		vis[u] = true;
		for (int v = 0; v <= n; v++) {
			if (vis[v] == false && G[u][v] != inf) {
				if (d[u] + G[u][v] < d[v]) {
					d[v] = d[u] + G[u][v];
					pre[v].clear();
					pre[v].push_back(u);
				}
				else if (d[u] + G[u][v] == d[v]) {
					pre[v].push_back(u);
				}
			}
		}
	}
}
int minNeed = inf,minBack=inf;
void dfs(int v) {
	tempath.push_back(v);
	if (v == 0) {
		int need = 0, back = 0;
		for (int i = tempath.size() - 1; i >= 0; i--) {
			int id = tempath[i];
			if (weight[id] > 0) {   //每到一個車站,只要超過full/2就需要回收
				back += weight[id];
			}
			else {   //不超過就需要補
				if (back > (0 - weight[id])) {  //回收的車就夠補
					back += weight[id];
				}
				else {  
					need += ((0 - weight[id]) - back); //回收的車不夠補就需要從中心帶出來
					back = 0;
				}
			}
		}
		if (need < minNeed) {
			minNeed = need;
			minBack = back;
			path = tempath;
		}
		else if (need == minNeed && back < minBack) {
			minBack = back;
			path = tempath;
		}
		tempath.pop_back();  //tempath爲全局變量,遞歸時push_back爲改變它,需要pop來返回原狀
		return;
	}
	for (int i = 0; i < pre[v].size(); i++)
		dfs(pre[v][i]);
	tempath.pop_back(); //同上
}
int main() {
	cin >> full >> n >> des >> m;
	for (int i = 1; i <= n; i++) {
		scanf("%d", &weight[i]);
		weight[i] =weight[i] - full / 2;
	}
	fill(G[0], G[0] + maxn * maxn, inf);
	while (m--) {
		int c1, c2, time;
		scanf("%d%d%d", &c1, &c2, &time);
		G[c1][c2] = G[c2][c1] = time;
	}
	Dijkstra();
	dfs(des);
	cout << minNeed << " ";
	for (int i = path.size() - 1; i >= 0; i--) {
		printf("%d", path[i]);
		if (i != 0) printf("->");
	}
	cout << " "<< minBack;
	return 0;	
}

1087 All Roads Lead to Rome (30分)

Dijkstra算法

#include<iostream>
#include<unordered_map>
#include<string>
#include<algorithm>
using namespace std;
const int inf = 1000000000;
int n, k, happy[200], G[200][200], d[200], num[200], h[200], pre[200];
double average[200];
bool vis[200];
string City[200];
int prelen(int v) {  //求當前路徑除了起點的長度
	int len = 0;
	if (v != 0) {
		v = pre[v];
		len = prelen(v) + 1;
	}
	return len;
}
void DFS(int v, int& des) {
	if (v == 0) {
		printf("%s->", City[v].c_str());
		return;
	}
	DFS(pre[v], des);
	printf("%s", City[v].c_str());
	if (v != des) printf("->");
}
int main() {
	freopen("1.txt", "r", stdin);
	scanf("%d%d", &n, &k);
	unordered_map<string, int> m; //城市名與序號的映射

	string s; cin >> s; m[s] = 0; City[0] = s;
	for (int i = 1; i <= n - 1; i++) {
		string city(3, 'A');
		scanf("%s%d", city.c_str(), &happy[i]);
		m[city] = i; City[i] = city;
	}
	fill(G[0], G[0] + 40000, inf);
	while (k--) {
		string c1(3, 'A'), c2(3, 'A'); int temp;
		scanf("%s%s%d", c1.c_str(), c2.c_str(), &temp);
		G[m[c1]][m[c2]] = G[m[c2]][m[c1]] = temp;
	}
	fill(d, d + 200, inf);
	d[0] = 0; num[0] = 1;
	for (int i = 0; i < n; i++) pre[i] = i;
	for (int i = 0; i < n; i++) {
		int u = -1, MIN = inf;
		for (int j = 0; j < n; j++) {
			if (vis[j] == false && d[j] < MIN) {
				u = j; MIN = d[j];
			}
		}
		if (u == -1) break;
		vis[u] = true;
		for (int v = 0; v < n; v++) {
			if (vis[v] == false && G[u][v] != inf) {
				if (d[u] + G[u][v] < d[v]) {
					d[v] = d[u] + G[u][v];
					h[v] = h[u] + happy[v];
					num[v] = num[u];
					pre[v] = u;
				}
				else if (d[u] + G[u][v] == d[v]) {
					num[v] += num[u];
					if (h[u] + happy[v] > h[v]) {
						h[v] = h[u] + happy[v];
						pre[v] = u;
					}
					else if (h[u] + happy[v] == h[v] && prelen(v) > prelen(u) + 1) { 
					//比較平均happy轉換爲比較路徑長度
						pre[v] = u;
					}
				}
			}
		}
	}
	int des = m["ROM"];
	printf("%d %d %d %d\n", num[des], d[des], h[des], h[des] / prelen(des));
	DFS(des, des);
	return 0;
}

Dijkstra+DFS算法

由於最短路徑的優化條件比較複雜(需要求路徑長度),還需要統計最短路徑條數,因此,使用Dijksatra+dfs的方法得到所求的最短路徑,其中Dijkstra算法用於獲得多條備選路徑,dfs對每一條路徑進行遍歷,然後根據優化尺度比較最終保留符合條件的路徑,這樣的解法更簡單。

#include<iostream>
#include<unordered_map>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
const int inf = 1000000000;
int n, k, happy[200], G[200][200], d[200];
bool vis[200];
string city[200];
vector<int> pre[200], temppath, path;

int optval = -1, cnt=0; //最優值(total happy), 路徑數
void dfs(int v) {
	temppath.push_back(v);
	if (v == 0) {
		cnt++;  //遞歸到起點則路徑數+1
		int val = 0;
		for (int i = 0; i < temppath.size(); i++) {
			val += happy[temppath[i]];
		}
		if (val > optval) {
			optval = val;
			path = temppath;
		}
		else if (val == optval && temppath.size() < path.size()) {
			path = temppath;
		}
		temppath.pop_back();
		return;
	}
	for(int i=0;i<pre[v].size();i++)
		dfs(pre[v][i]);
	temppath.pop_back();
}
int main() {
# ifdef ONLINE_JUDGE
# else 
	freopen("1.txt", "r", stdin);
# endif
	scanf("%d%d", &n, &k);
	unordered_map<string, int> m; //城市名與序號的映射
	string s; cin >> s; m[s] = 0; city[0] = s;
	for (int i = 1; i <= n - 1; i++) {
		string temp;
		cin >> temp >> happy[i];
		m[temp] = i; city[i] = temp;
	}
	fill(G[0], G[0] + 40000, inf);
	while (k--) {
		string c1, c2; int temp;
		cin >> c1 >> c2 >> temp;
		G[m[c1]][m[c2]] = G[m[c2]][m[c1]] = temp;
	}
	fill(d, d + 200, inf);
	d[0] = 0;
	
	for (int i = 0; i < n; i++) {
		int u = -1, MIN = inf;
		for (int j = 0; j < n; j++) {
			if (vis[j] == false && d[j] < MIN) {
				u = j; MIN = d[j];
			}
		}
		if (u == -1) break;
		vis[u] = true;
		for (int v = 0; v < n; v++) {
			if (vis[v] == false && G[u][v] != inf) {
				if (d[u] + G[u][v] < d[v]) {
					d[v] = d[u] + G[u][v];
					pre[v].clear();
					pre[v].push_back(u);
				}
				else if (d[u] + G[u][v] == d[v]) {
					pre[v].push_back(u);
				}
			}
		}
	}
	int des = m["ROM"];
	dfs(des);
	printf("%d %d %d %d\n", cnt, d[des], optval, optval / (path.size() - 1));
	for (int i = path.size() - 1; i >= 0; i--) {
		cout << city[path[i]];
		if (i != 0) cout << "->";
	}
	return 0;
}

1072 Gas Station (30分)

分析

這道題前面的題不太一樣,前面都是在求最短路徑,而這道題求解的就是某點到其他點的最短距離。

犯的低級錯誤

將輸入c1,c2轉換爲數組下標時,數值部分只取了一位(也不知道是怎麼想的),導致最後一個測試點出錯,還以爲是算法的錯誤看了半天。

AC代碼

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
const int maxn = 1015, inf = 1000000000;
int G[maxn][maxn], d[maxn], MIN[15], sum[15];
bool vis[maxn];
int main() {
	//freopen("1.txt", "r", stdin);
	int n, m, k, ds;
	cin >> n >> m >> k >> ds;
	fill(G[0], G[0] + maxn * maxn, inf);
	while (k--) {
		string c1, c2; int temp;
		cin >> c1 >> c2 >> temp;
		int t1, t2;
		if (c1[0] == 'G') t1 = stoi(c1.substr(1)) + n;
		else t1 = stoi(c1);
		if (c2[0] == 'G') t2 = stoi(c2.substr(1)) + n;
		else t2 = stoi(c2);
		G[t1][t2] = G[t2][t1] = temp;
	}
	fill(MIN + 1, MIN + m + 1, inf);
	for (int s = n + 1; s <= n + m; s++) {
		fill(vis,vis+maxn,false);
		fill(d, d + maxn, inf);
		d[s] = 0;
		for (int i = 0; i < n + m; i++) {
			int u = -1, MIN = inf;
			for (int j = 1; j <= n + m; j++) {
				if (vis[j] == false && d[j] < MIN) {
					u = j; MIN = d[j];
				}
			}
			if (u == -1) break;
			vis[u] = true;
			for (int v = 1; v <= n + m; v++) {
				if (vis[v] == false && G[u][v] != inf && d[v] > d[u] + G[u][v]) {
					d[v] = d[u] + G[u][v];
				}
			}
		}
		int id = s - n;
		for (int i = 1; i <= n; i++) {
			if (d[i] > ds) {
			    MIN[id]=-1;
			    break;
			}
			if (d[i] < MIN[id]) MIN[id] = d[i];
			sum[id] += d[i];
		}
	}
	int id, Min=-1, Sum=inf;
	for (int i = 1; i <= m; i++) {
		if (MIN[i] > Min) {
			Min = MIN[i];
			Sum = sum[i];
			id = i;
		}
		else if (MIN[i] == Min && Sum > sum[i]) {
			Sum = sum[i];
			id = i;
		}
	}
	if (Min == -1) printf("No Solution");
	else printf("G%d\n%.1f %.1f", id, Min*1.0, Sum*1.0 / n);
	return 0;
}

1111 Online Map (30分)

分析

模板題,但是需要兩個不同的Dijkstra和DFS函數,代碼量有點大。

#include<iostream>
#include<cstring>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn = 505, inf=0x3fffffff;
int n, m, s, des;
int G[maxn][maxn], vis[maxn], d[maxn], Time[maxn][maxn], t[maxn];
vector<int> pre1[maxn],pre2[maxn];
void Dijkstra1() {
	fill(d, d + maxn, inf);
	d[s] = 0;
	for (int i = 0; i < n; i++) {
		int u = -1, MIN = inf;
		for (int j = 0; j < n; j++) {
			if (vis[j] == false && d[j] < MIN) {
				u = j; MIN = d[j];
			}
		}
		if (u == -1) return;
		vis[u] = true;
		for (int v = 0; v < n; v++) {
			if (vis[v] == false && G[u][v] != inf) {
				if (d[u] + G[u][v] < d[v]) {
					d[v] = d[u] + G[u][v];
					pre1[v].clear();
					pre1[v].push_back(u);
				}
				else if (d[u] + G[u][v] == d[v]) {
					pre1[v].push_back(u);
				}
			}
		}
	}
}
void Dijkstra2() {
	fill(t, t + maxn, inf);
	t[s] = 0;
	for (int i = 0; i < n; i++) {
		int u = -1, MIN = inf;
		for (int j = 0; j < n; j++) {
			if (vis[j] == false && t[j] < MIN) {
				u = j; MIN = t[j];
			}
		}
		if (u == -1) return;
		vis[u] = true;
		for (int v = 0; v < n; v++) {
			if (vis[v] == false && Time[u][v] != inf) {
				if (t[u] + Time[u][v] < t[v]) {
					t[v] = t[u] + Time[u][v];
					pre2[v].clear();
					pre2[v].push_back(u);
				}
				else if (t[u] + Time[u][v] == t[v]) {
					pre2[v].push_back(u);
				}
			}
		}
	}
}
vector<int> path1, path2, temppath;
int minTime = inf, minnum = inf;
void dfs1(int v) {
	temppath.push_back(v);
	if (v == s) {
		int tempTime=0;
		for (int i = temppath.size() - 1; i >= 1; i--) {
			tempTime += Time[temppath[i]][temppath[i-1]];
		}
		if (tempTime < minTime) {
			minTime = tempTime;
			path1 = temppath;
		}
	}
	for (int i = 0; i < pre1[v].size(); i++) {
		dfs1(pre1[v][i]);
	}
	temppath.pop_back();
}
void dfs2(int v) {
	temppath.push_back(v);
	if (v == s) {
		
		if (temppath.size() < minnum) {
			minnum = temppath.size();
			path2 = temppath;
		}
	}
	for (int i = 0; i < pre2[v].size(); i++) {
		dfs2(pre2[v][i]);
	}
	temppath.pop_back();
}
int main() {
#ifdef ONLINE_JUDGE
#else
	freopen("1.txt", "r", stdin);
#endif	
	cin >> n >> m;
	fill(G[0], G[0] + maxn * maxn, inf);
	fill(Time[0], Time[0] + maxn * maxn, inf);
	while (m--) {
		int c1, c2, flag, d, t;
		scanf("%d%d%d%d%d", &c1, &c2, &flag, &d, &t);
		G[c1][c2] = d; Time[c1][c2] = t;
		if (!flag) {
			G[c2][c1] = d;
			Time[c2][c1] = t;
		}
	}
	cin >> s >> des;
	Dijkstra1(); memset(vis, 0, sizeof(vis));
	Dijkstra2();
	dfs1(des); dfs2(des);
	if (path1 != path2) {
		printf("Distance = %d: ", d[des]);
		for (int i = path1.size() - 1; i >= 0; i--) {
			if (i != path1.size() - 1) printf(" -> ");
			printf("%d", path1[i]);
		}
		cout << endl;
		printf("Time = %d: ", t[des]);
		for (int i = path2.size() - 1; i >= 0; i--) {
			if (i != path2.size() - 1) printf(" -> ");
			printf("%d", path2[i]);
		}
	}
	else {
		printf("Distance = %d; ", d[des]);
		printf("Time = %d: ", t[des]);
		for (int i = path1.size() - 1; i >= 0; i--) {
			if (i != path1.size() - 1) printf(" -> ");
			printf("%d", path1[i]);
		}
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章