【PAT-A1087】 All Roads Lead to Rome(带第二标尺的dijkstra最短路)

【分析】

这题就是典型的带第二标尺(幸福度)和第三标尺(平均幸福度)的最短路径,同时要记录最短路径并输出。
做这题又复习了一遍Dijkstra算法。算法题就是在理解的基础上训练的结果,每次在问题中回顾都会有新的收获,对于这类问题和算法的理解也会更深刻。
开始没太仔细思考平均幸福度这一标尺,就用单纯的Dijkstra算法求解,后来才发现平均幸福度不太容易随着求解过程更新,因为平均是对整条路平均,所以要得到最短路径的长度。当然,后面看了题解其实也好解决,只要用一个新的数组记录当前结点最短路径的结点数即可。
因此,我又回顾了Dijkstra+DFS的方法。这种方法的精髓就是:Dijkstra只管求出所有基于第一标尺的最短路径并保存到vector path[maxn]中,至于选出第二、第三甚至第四标尺下最优路径都交给DFS遍历所有路径的时候来求解。注意求最短路径就单纯地求,不要同时求第二标尺,这样会比较混乱。

【代码】

#include <cstdio>
#include <algorithm>
#include <map>
#include <string>
#include <vector>
#include <iostream>
using namespace std;
const int maxn = 210;
const int INF = 1 << 31 - 1;
int G[maxn][maxn] = { 0 }, cost, d[maxn] = { 0 }, w[maxn] = { 0 };
int num[maxn], maxW = 0;
vector<int> pre[maxn], path, temp_path;
char start[4], city1[4], city2[4];
string st, c1, c2, tmp;
int happiness[maxn];
int n, k;
double maxAve = -1.0;
map<string, int> mp;
map<int, string> mp1;

void Dijkstra(int s) {
	fill(d, d + maxn, INF);
	fill(num, num + maxn, 0);
	bool vis[maxn] = { false };
	d[s] = 0;
	num[s] = 1;
	for (int i = 0; i < n; i++)
	{
		int MIN = INF, u = -1;
		for (int j = 0; j < n; j++)
		{
			if (vis[j] == false && d[j] < MIN) {
				MIN = d[j];
				u = j;
			}

		}
		if (u == -1) return; // 剩余结点不连通
		vis[u] = true;
		// 更新可达d[i];
		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] = w[u] + happiness[v];
					num[v] = num[u];
					pre[v].clear();
					pre[v].push_back(u);
				}
				else if (d[u] + G[u][v] == d[v]) {
					if (w[v] < w[u] + happiness[v]) {
						w[v] = w[u] + happiness[v];
					}
					num[v] += num[u];
					pre[v].push_back(u);
				}

			}
		}

	}
	

}

void DFS(int v, int st) {
	if (v == st) {
		temp_path.push_back(v);
		// 计算权值和以及平均值
		int tempW = 0;
		for (int i = temp_path.size() - 2; i >= 0; i--)
		{
			tempW += happiness[temp_path[i]];
		}
		double t = 1.0 * tempW / (temp_path.size() - 1);
		if (tempW > maxW) {
            maxW = tempW;
			maxAve = t;
			path = temp_path;
		}
        else if(tempW == maxW && t > maxAve) {
            maxAve = t;
			path = temp_path;
        }
		temp_path.pop_back();
		return;
	}

	temp_path.push_back(v);
	for (int i = 0; i < pre[v].size(); i++)
	{
		DFS(pre[v][i], st);
	}

	temp_path.pop_back();
}
int main() {
	fill(G[0], G[0] + maxn * maxn, INF);
    fill(happiness, happiness + maxn, 0);
	// scanf("%d%d%s", &n, &k, start);
	cin >> n >> k >> st;
	mp[st] = 0;
	mp1[0] = st;
	for (int i = 0; i < n - 1; i++)
	{
		cin >> tmp >> happiness[i + 1];
		mp[tmp] = i + 1;
		mp1[i + 1] = tmp;
	}
	for (int i = 0; i < k; i++)
	{
		cin >> c1 >> c2 >> cost;
		G[mp[c1]][mp[c2]] = cost;
		G[mp[c2]][mp[c1]] = cost;
	}

	Dijkstra(0);
	DFS(mp["ROM"], 0);
	cout << num[mp["ROM"]] << ' ' << d[mp["ROM"]] << ' ' << w[mp["ROM"]] << ' ' << (int)maxAve << endl;
	for (int i = path.size() - 1; i >= 0; i--)
	{
		
		cout << mp1[path[i]];
		if (i != 0) cout << "->";
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值