程序设计天梯赛L3-11 (Dijkstra模板)

题目
题意: 给定n个点,m条边。每个点有对应的点权。输出对应的最短路。如果有多条最短路,则输出经过点数最多的路径,若仍有多条,输出点权和最大的路径。而且输入的点是以字符串形式给出的,要拿map映射一下,不过还好n很小,n <= 200.
思路: 拿Dij跑一下即可,感觉写起来很麻烦,也可能我写麻烦了。
时间复杂度: O((n+m)logn + mlogm)
代码:

#include<bits/stdc++.h>
using namespace std;
#define mem(a,x) memset(a,x,sizeof(a))
typedef pair<int,int> PII;
const int N = 202;
map<string,int> mp;
map<int,string> mp2;
int dist[N];
bool vis[N];
int cnt[N]; //最短路条数
int num[N]; //点数
int sum[N]; //点权和
int a[N];
int h[N],e[N<<1],ne[N<<1],w[N<<1],idx = 0;
int pre[N];
int n,m,k,T;
string st,ed;
//最短路、点数、点权
int tot = 0;
void add(int a,int b,int c)
{
	e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}
void Dij(int S)
{
	mem(vis,false);
	mem(dist,0x3f);
	dist[S] = 0;
	priority_queue<PII,vector<PII>,greater<PII> > q;
	q.push({0,S});
	cnt[S] = 1;
	while(q.size())
	{
		PII tmp = q.top(); q.pop();
		int u = tmp.second,dis = tmp.first;
		if(dis != dist[u]) continue;
		for(int i=h[u];~i;i=ne[i])
		{
			int j = e[i];
			if(dist[j] > dist[u] + w[i])
			{
				dist[j] = dist[u] + w[i];
				cnt[j] = cnt[u];
				num[j] = num[u] + 1;
				sum[j] = sum[u] + a[j];
				// cout<<j<<":"<<a[j]<<endl;
				// cout<<j<<":"<<sum[j]<<endl;
				pre[j] = u;
				q.push({dist[j],j});
			}
			else if(dist[j] == dist[u] + w[i])
			{
				cnt[j] += cnt[u];
				if(num[j] < num[u] + 1)
				{
					num[j] = num[u] + 1;
					sum[j] = sum[u] + a[j];
					pre[j] = u;
				}
				else if(num[j] == num[u] + 1)
				{
					if(sum[j] < sum[u] + a[j])
					{
						num[j] = num[u] + 1;
						sum[j] = sum[u] + a[j];
						pre[j] = u;
					}
				}
			}
		}
	}
}
void solve()
{
	memset(h,-1,sizeof(h));
	memset(pre,-1,sizeof(pre));
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n>>m;
	cin>>st>>ed;
	mp[st] = ++tot,mp[ed] = ++tot;
	mp2[1] = st,mp2[2] = ed;
	for(int i=0;i<n-1;++i)
	{
		string s; cin>>s; int x; cin>>x;
		if(mp[s]) ;
		else
		{
			mp[s] = ++tot;
			mp2[tot] = s;
		}
		a[mp[s]] = x;
	}
	while(m--)
	{
		string a,b; int z;
		cin>>a>>b>>z;
		int x = mp[a];
		int y = mp[b];
		add(x,y,z);
		add(y,x,z);
	}
	Dij(mp[st]);
	vector<string> va;
    int now = mp[ed];
	while(now != -1)
	{
		va.push_back(mp2[now]);
		now = pre[now];
	}
	reverse(va.begin(),va.end());
	for(int i=0;i<va.size();++i)
	{
		if(i) cout<<"->";
		cout<<va[i];
	}
	cout<<"\n";
	cout<<cnt[mp[ed]]<<" "<<dist[mp[ed]]<<' '<<sum[mp[ed]];
}
signed main(void)
{
	solve();
	return 0;
}
Dijkstra算法是一种用于解决带权图的单源最短路径问题的贪心算法。它维护一个距离起点的最短路径已知的顶点集合,通过不断地扩展这个集合,最终得到从起点到所有顶点的最短路径。 Dijkstra算法的基本思想是,维护一个集合S,表示已经求出最短路径的顶点集合。一开始,S只包含起点。然后,每次从集合V-S中选取一个距离起点最近的顶点u,将其加入集合S中,并更新与u相邻的所有顶点的最短路径。 具体实现上,我们可以使用一个数组dis[]来存储每个顶点到起点的最短路径长度,数组vis[]表示该顶点是否已经被加入到集合S中。每次选取距离起点最近的顶点u后,我们遍历u的所有邻居v,并更新dis[v]的值,如果dis[v]发生了改变,我们就将v加入到一个优先队列中,等待下一次选择。 以下是Dijkstra算法的伪代码实现: ``` int n; // 顶点数 int dis[N]; // 存储起点到每个顶点的最短距离 bool vis[N]; // 标记每个顶点是否已经加入集合S中 vector<pair<int, int>> adj[N]; // 存储每个顶点的邻居 void dijkstra(int s) { // s为起点编号 memset(dis, 0x3f, sizeof dis); // 将dis数组初始化为无穷大 dis[s] = 0; // 起点到自身的距离为0 priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q; q.push({0, s}); // 将起点加入队列中 while (!q.empty()) { auto t = q.top(); q.pop(); int u = t.second; if (vis[u]) continue; // 如果该点已经在集合S中,直接跳过 vis[u] = true; // 将u加入集合S中 for (auto [v, w] : adj[u]) { // 遍历u的所有邻居 if (dis[v] > dis[u] + w) { // 如果从u到v的距离更短 dis[v] = dis[u] + w; // 更新dis数组 q.push({dis[v], v}); // 将v加入队列中 } } } } ``` 其中,priority_queue是一个优先队列,用于存储待选顶点。我们使用了STL中的pair来表示顶点与其到起点的距离。优先队列默认按照pair的第一个元素排序,因此我们需要自定义一个比较函数,将pair按照第二个元素(距离)排序。 Dijkstra算法的时间复杂度为O(ElogV),其中E为边数,V为顶点数。在实际应用中,Dijkstra算法的效率很高,能够处理大规模的图。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值