Bootstrap

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;
}
;