PAT甲级——最短路总结

1003 Emergency (25 分)

题目大意
有 n 个城市和 m 条路,给定起点和终点,需要你求出从起点到终点的最短距离,输出最短路径的条数和在这几条最短的路径中救援小组之和最大的值。

分析
用 dijkstra 算法求最短路,并更新 dis 数组,cnt 数组和 num 数组,其中 dis 数组用于存储到达某个点时的最短距离,cnt 用于存储该点对应的最短路径的数量,num 用于存储救援队的数目。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int maxn = 510;
const int inf = 0x3f3f3f3f;
int n, m, c1, c2;
int f[maxn], dis[maxn], cnt[maxn], num[maxn];	// dis 用于存储最短路径,cnt 用于存储最短路径数量,num 用于存储救援队的数目 
bool vis[maxn];

struct node{
	int to, len;
};

vector<node> v[maxn];

void bfs()
{
	memset(dis, inf, sizeof(dis));
	memset(vis, false, sizeof(vis));
	priority_queue<pii, vector<pii>, greater<pii> > q;
	q.push(pii(0, c1));
	dis[c1] = 0;
	cnt[c1] = 1;
	num[c1] = f[c1]; 
	while(!q.empty()){
		pii st = q.top();
		int p = st.second;
		q.pop();
		if(vis[p])
			continue;
		vis[p] = true;
		for(int i = 0; i < v[p].size(); i++){
			int tt = v[p][i].to;
			if(dis[tt] > dis[p] + v[p][i].len){
				dis[tt] = dis[p] + v[p][i].len;
				num[tt] = num[p] + f[tt];
				cnt[tt] = cnt[p];
				q.push({dis[tt], tt});	
			}
			else if(dis[tt] == dis[p] + v[p][i].len){
				cnt[tt] += cnt[p];
				if(num[tt] < num[p] + f[tt]){
					num[tt] = num[p] + f[tt];
				}
			} 
		}
	}
}

int main()
{
	cin >> n >> m >> c1 >> c2;
	for(int i = 0; i < n; i++)
		cin >> f[i];
	for(int i = 0; i < m; i++){
		int a, b, c;
		cin >> a >> b >> c;
		v[a].push_back({b, c});
		v[b].push_back({a, c});
	}
	bfs();
	cout << cnt[c2] << " " << num[c2] << endl;
	return 0;
}

1018 Public Bike Management (30 分)

题目大意
每个车站的最大容量为cmax,如果一个车站里自行车的数量刚好为 cmax / 2,则可以称其为完美状态。控制中心(结点 0)会携带或从路上收集一定数量的自行车前往某个目标车站,并保证沿途经过的车站都能达到完美状态,需要你求出最短路径,如果存在多个最短路径,则求出携带自行车数量最少的那条,如果还是存在多条,那么就给出从车站带回的自行车数量最少的那条路径。

分析
用 dijkstra 先算出最短路,然后用 dfs 求最小需要携带的数量和最小需要带回的数量,并更新 ans 即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 505;
const int inf = 0x3f3f3f3f;
int c, n, sp, m, minback, minneed;
int s[maxn], dis[maxn];
vector<int> pre[maxn], path, ans;

struct node{
	int to, val;
};

vector<node> v[maxn];

void dfs(int x)
{
	if(x == 0){
		int back = 0, need = 0;
		for(int i  = path.size() - 1; i >= 0; i--){
			int id = path[i];
			if(s[id] > 0)
				back += s[id];
			else{
				if(back + s[id] > 0)
					back += s[id];
				else{
					need -= (back + s[id]);
					back = 0;
				}
			}
		}
		if(minneed > need || (minneed == need && minback > back)){
			minneed = need;
			minback = back;
			ans = path;
		}
		return ;
	}
	path.push_back(x);
	for(int i = 0; i < pre[x].size(); i++)
		dfs(pre[x][i]);
	path.pop_back();
	return ;
}

void dijkstra()
{
	memset(dis, inf, sizeof(dis));
	dis[0] = 0;
	queue<int> q;
	q.push(0);
	int mn = inf;
	while(!q.empty()){
		int p = q.front();
		q.pop();
		for(int i = 0; i < v[p].size(); i++){
			int tt = v[p][i].to;
			if(dis[tt] > dis[p] + v[p][i].val){
				dis[tt] = dis[p] + v[p][i].val;
				pre[tt].clear();
				pre[tt].push_back(p);
				q.push(tt);
			}
			else if(dis[tt] == dis[p] + v[p][i].val)
				pre[tt].push_back(p);
		}
	}
}

int main()
{
	cin >> c >> n >> sp >> m;
	for(int i = 1; i <= n; i++){
		cin >> s[i];
		s[i] = s[i] - (c / 2);
	}
	for(int i = 1; i <= m; i++){
		int a, b, c;
		cin >> a >> b >> c;
		v[a].push_back({b, c});
		v[b].push_back({a, c}); 
	}
	dijkstra();
	minback = inf, minneed = inf;
	dfs(sp);
	printf("%d 0", minneed);
	for(int i = ans.size() - 1; i >= 0; i--)
		printf("->%d", ans[i]);
	printf(" %d\n", minback);
	return 0;
}

1030 Travel Plan (30 分)

题目大意
求出起点到终点的最短路径和花费,输出最短的路径,若存在多条最短路径,则输出花费最少的路径。

分析
用 dijkstra 求最短路径和花费,同时用 pre 记录遍历到的结点的前一个结点,然后用 dfs 递归输出即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 505;
const int inf = 0x3f3f3f3f;
int n, m, s, d, dis[maxn], cost[maxn];
map<int, int> pre;

struct node{
	int to, len, val;
};

vector<node> v[maxn];

void dijkstra()
{
	memset(dis, inf, sizeof(dis));
	memset(cost, inf, sizeof(cost));
	dis[s] = 0;
	cost[s] = 0;
	queue<int> q;
	q.push(s);
	while(!q.empty()){
		int p = q.front();
		q.pop();
		for(int i = 0; i < v[p].size(); i++){
			int tt = v[p][i].to;
			if(dis[tt] > dis[p] + v[p][i].len){
				dis[tt] = dis[p] + v[p][i].len;
				pre[tt] = p;
				cost[tt] = cost[p] + v[p][i].val;
				q.push(tt);
			}
			else if(dis[tt] == dis[p] + v[p][i].len){
				if(cost[tt] > cost[p] + v[p][i].val){
					cost[tt] = cost[p] + v[p][i].val;
					pre[tt] = p;
					q.push(tt);
				}
			}
		}
	}
}

void dfs(int v)
{
	if(v == s){
		printf("%d ", v);
		return ;
	}
	dfs(pre[v]);
	printf("%d ", v);
}

int main()
{
	cin >> n >> m >> s >> d;
	for(int i = 0; i < m; i++){
		int a, b, l, w;
		cin >> a >> b >> l >> w;
		v[a].push_back({b, l, w});
		v[b].push_back({a, l, w});
	}
	dijkstra();
	dfs(d);
	printf("%d %d\n", dis[d], cost[d]);
	return 0;
}

1072 Gas Station (30 分)

题目大意
给定 m 个加油站,编号为 G1 ~ Gm,从 m 个加油站中选择一个,使其离住房最近的距离最远,并且没有超出服务范围,如果有多个最远的加油站,则选择距离居民区平均距离最小的那个,如果平均距离还是相同,就选择加油站编号最小的。

分析
用 dijkstra 来求出从加油站到各个住房的最短路,并存在 dis 数组中,更新最近最远距离及平均距离即可,需要注意的是,因为加油站是用 G1 ~ Gm 表示的,而住房数小于等于 1000,所以我们可以用 1000 以后的数字来表示加油站,如 1001 ~ 1000 + m。

#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 1e3 + 20;
int n, m, k, ds;
int mxAns, mnSum, dis[maxn];

struct node{
	int to, len;
};

vector<node> v[maxn];

void bfs(int s)
{
	memset(dis, inf, sizeof(dis));
	queue<int> q;
	q.push(s);
	dis[s] = 0;
	while(!q.empty()){
		int p = q.front();
		q.pop();
		for(int i = 0; i < v[p].size(); i++){
			int tt = v[p][i].to;
			if(dis[tt] > dis[p] + v[p][i].len){
				dis[tt] = dis[p] + v[p][i].len;
				q.push(tt);
			}
		}
	}
}

int main()
{
	cin >> n >> m >> k >> ds;
	for(int i = 1; i <= k; i++){
		string a, b;
		int x, y, z;
		cin >> a >> b >> x;
		if(a[0] == 'G')
			y = 1000 + stoi(a.substr(1));
		else
			y = stoi(a);
		if(b[0] == 'G')
			z = 1000 + stoi(b.substr(1));
		else
			z = stoi(b);
		v[y].push_back({z, x});
		v[z].push_back({y, x});
	}
	mxAns = 0, mnSum = inf;
	int flag = 0, index = 0;
	for(int i = 1001; i <= 1000 + m; i++){
		int sum = 0;
		bfs(i);
		int mn = inf, vis = 1;
		for(int j = 1; j <= n; j++){
			if(dis[j] == inf || dis[j] > ds){
				vis = 0;
				break;
			}
			sum += dis[j];
			mn = min(mn, dis[j]);
		}
		if(!vis)
			continue;
		flag = 1;
		if(mxAns < mn || (mxAns == mn && mnSum > sum)){
			index = i - 1000;
			mnSum = sum;
			mxAns = mn;
		}	
	}
	if(!flag)
		cout << "No Solution" << endl;
	else{
		cout << "G" + to_string(index) << endl;
		printf("%.1f %.1f\n", 1.0 * mxAns, 1.0 * mnSum / n);
	}
	return 0;
}

1087 All Roads Lead to Rome (30 分)

题目大意
有 n 个城市,k 条无向边,给定起始城市,求其到 ROM 的最少花费,并输出其路径。若存在多条,就输出幸福值最大的那条,如果仍不唯一,就输出平均幸福值最大的那条路径。

分析
用 dijkstra 算出最少花费,存入 dis 数组中,同时更新 sum 数组,cnt 数组,num 数组,并记录前驱即可。(sum 数组用于存储幸福值,cnt 数组用于存储路径条数,num数组用于存储当前城市为该路径上的第几座城市)。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int maxn = 210;
const int inf = 0x3f3f3f3f;
int n, k, tol, start, ed, f[maxn], dis[maxn], sum[maxn], cnt[maxn], num[maxn];
string s;
bool vis[maxn];
map<string, int> mp;
map<int, string> name;
map<int, int> pre;

struct node{
	int to, cost;
};

vector<node> v[maxn];

void bfs()
{
	memset(dis, inf, sizeof(dis));
	memset(vis, false, sizeof(vis));
	dis[start] = 0;
	sum[start] = 0;
	cnt[start] = 1;
	num[start] = 0;
	priority_queue<pii, vector<pii>, greater<pii> > q;
	q.push({0, start});
	while(!q.empty()){
		pii p = q.top();
		q.pop();
		int m = p.second;
		if(vis[m])
			continue;
		vis[m] = true;
		for(int i = 0; i < v[m].size(); i++){
			int tt = v[m][i].to;
			if(dis[tt] > dis[m] + v[m][i].cost){
				dis[tt] = dis[m] + v[m][i].cost;
				sum[tt] = sum[m] + f[tt];
				cnt[tt] = cnt[m];
				num[tt] = num[m] + 1; 
				pre[tt] = m;
				q.push({dis[tt], tt});
			}
			else if(dis[tt] == dis[m] + v[m][i].cost){
				cnt[tt] += cnt[m];
				if(sum[tt] < sum[m] + f[tt]){
					sum[tt] = sum[m] + f[tt];
					num[tt] = num[m] + 1;
					pre[tt] = m;
				}
			}
		}
	}
}

void print(int x)
{
	if(x == start){
		cout << name[x];
		return ;
	}
	print(pre[x]);
	cout << "->" << name[x];
}

int main()
{
	cin >> n >> k >> s;
	mp[s] = tol++;
	name[tol-1] = s;
	start = 0;
	for(int i = 0; i < n - 1; i++){
		string city;
		int x;
		cin >> city >> x;
		mp[city] = tol++;
		f[mp[city]] = x;
		name[tol-1] = city;
		if(city == "ROM")
			ed = mp[city];
	}
	for(int i = 0; i < k; i++){
		string city1, city2;
		int y;
		cin >> city1 >> city2 >> y;
		v[mp[city1]].push_back({mp[city2], y});
		v[mp[city2]].push_back({mp[city1], y});
	}
	bfs();
	printf("%d %d %d %d\n", cnt[ed], dis[ed], sum[ed], sum[ed] / num[ed]);
	print(ed);
	return 0;
}

1111 Online Map (30 分)

题目大意
给定 n 个点,m 条街道,求最短路径和时间花费最小的路径,若存在多条最短路径,则输出花费时间最小的那条,若存在多条时间花费最小的路径,就输出经过的顶点数量最少的那条。(还要注意一下 one-way 是否为 1,若为 1,则只能从 v1 到 v2,若为 0,则 v1 和 v2 相互之间都能到达)。

分析
先用一次 dijkstra 求出最短路径,同时维护用于存储最小时间花费的数组 cost,并记录下各个结点的前驱,然后通过 dfs 将得到的路径存储到 path1 中,再用一次 dijkstra 求出最小时间花费,同时维护用于存储经过顶点数量的数组 num,并记录下各个结点的前驱,通过 dfs 将得到的路径存储到 path2 中,比较 path1 和 path2 是否相同,再根据题意输出答案即可。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int maxn = 510;
const int inf = 0x3f3f3f3f;
int n, m, st, ed, dis[maxn], cost[maxn], t[maxn], num[maxn];
bool vis[maxn];
map<int, int> pre1, pre2;
vector<int> path1, path2;
struct node{
	int to, len, tim;
};

vector<node> v[maxn];

void dijkstra1()
{
	memset(dis, inf, sizeof(dis));
	memset(vis, false, sizeof(vis));
	dis[st] = 0;
	cost[st] = 0;
	priority_queue<pii, vector<pii>, greater<pii> > q;
	q.push({0, st});
	while(!q.empty()){
		pii p = q.top();
		q.pop();
		int m = p.second;
		if(vis[m])
			continue;
		vis[m] = true;
		for(int i = 0; i < v[m].size(); i++){
			int tt = v[m][i].to;
			if(dis[tt] > dis[m] + v[m][i].len){
				dis[tt] = dis[m] + v[m][i].len;
				cost[tt] = cost[m] + v[m][i].tim;
				q.push({dis[tt], tt});
				pre1[tt] = m;
			}
			else if(dis[tt] == dis[m] + v[m][i].len){
				if(cost[tt] > cost[m] + v[m][i].tim){
					cost[tt] = cost[m] + v[m][i].tim;
					pre1[tt] = m;
				}
			}
		}
	}
}

void dijkstra2()
{
	memset(t, inf, sizeof(t));
	memset(vis, false, sizeof(vis));
	t[st] = 0;
	num[st] = 0;
	priority_queue<pii, vector<pii>, greater<pii> > q;
	q.push({0, st});
	while(!q.empty()){
		pii p = q.top();
		q.pop();
		int m = p.second;
		if(vis[m])
			continue;
		vis[m] = true;
		for(int i = 0; i < v[m].size(); i++){
			int tt = v[m][i].to;
			if(t[tt] > t[m] + v[m][i].tim){
				t[tt] = t[m] + v[m][i].tim;
				num[tt] = num[m] + 1;
				q.push({t[tt], tt});
				pre2[tt] = m;
			}
			else if(t[tt] == t[m] + v[m][i].tim){
				if(num[tt] > num[m] + 1){
					num[tt] = num[m] + 1;
					pre2[tt] = m; 
				}
			}
		}
	}
}

void dfs1(int x)
{
	if(x == st){
		path1.push_back(x);
		return ;
	}
	dfs1(pre1[x]);
	path1.push_back(x);
}

void dfs2(int x)
{
	if(x == st){
		path2.push_back(x);
		return ;
	}
	dfs2(pre2[x]);
	path2.push_back(x);
}

int main()
{
	cin >> n >> m;
	for(int i = 0; i < m; i++){
		int a, b, c, d, e;
		cin >> a >> b >> c >> d >> e;
		if(c == 1)
			v[a].push_back({b, d, e});
		else{
			v[a].push_back({b, d, e});
			v[b].push_back({a, d, e});
		}
	}
	cin >> st >> ed;
	dijkstra1();
	dfs1(ed);
	dijkstra2();
	dfs2(ed);
	int flag = 1;
	if(path1.size() != path2.size())
		flag = 0;
	else{
		for(int i = 0; i < path1.size(); i++){
			if(path1[i] != path2[i]){
				flag = 0;
				break;
			}
		}
	}
	if(flag){
		printf("Distance = %d; Time = %d: %d", dis[ed], t[ed], st);
		for(int i = 1; i < path1.size(); i++)
			printf(" -> %d", path1[i]);
		printf("\n");
	}
	else{
		printf("Distance = %d: %d", dis[ed], st);
		for(int i = 1; i < path1.size(); i++)
			printf(" -> %d", path1[i]);
		printf("\n");
		printf("Time = %d: %d", t[ed], st);
		for(int i = 1; i < path2.size(); i++)
			printf(" -> %d", path2[i]);
		printf("\n");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值