地铁 CSU - 1808 dijkstra 拆点

题解

每个点有多个线路,不同线路之间切换需要额外代价,直接在队列的节点中记录当前线路编号增加额外代价无法得到最优解。
将原有节点拆分,使用map进行标号,如果当前节点有x个线路相连则将当前节点拆分为x个点,原边只在相同线路的点副本之间连接。
**每个节点,相邻站点编号的副本节点进行相连,代价为编号差。**不用每个副本都两两之间建立边,会导致极端复杂度M^2。
起始节点和终点的副本之间建立0代价边。表示从任意编号起点出发,到任意编号终点结束。最后dijkstra~。

AC代码

#include <stdio.h>
#include <bits/stdc++.h>
#define fst first
#define sed second
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e5 + 10;
int n, m;
map<pair<int, int>, int> mp;
int idx;
bool vis[N];
ll dis[N];

struct node
{
	int v;
	ll w;
	bool operator < (const node &o) const
	{
		return w > o.w;
	}
};
vector<node> e[N * 3];
vector<int> t[N]; //每个节点车站数量

int code(int v, int c) //给节点编号
{
	int &res = mp[{v, c}];
	if (!res)
		res = ++idx;
	return res;
}
ll dijkstra()
{
	memset(vis, 0, sizeof(vis));
	memset(dis, 0x3f, sizeof(dis));
	int st = code(1, t[1][0]), ed = code(n, t[n][0]); //起点终点任意车站编号
	priority_queue<node> pq;
	pq.push({ st, 0 });
	dis[st] = 0;
	while (!pq.empty())
	{
		int u = pq.top().v;
		if (u == ed)
			return pq.top().w;
		pq.pop();
		vis[u] = 1;
		for (auto it : e[u])
		{
			int v = it.v;
			ll w = it.w;
			if (!vis[v] && dis[u] + w < dis[v])
				dis[v] = dis[u] + w, pq.push({ v, dis[v] });
		}
	}
	return -1;
}
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	while (cin >> n >> m)
	{
		mp.clear();
		idx = 0;
		for (int i = 0; i < m; ++i)
		{
			int u, v, c, w;
			scanf("%d%d%d%d", &u, &v, &c, &w);
			t[u].push_back(c); //记录车站
			t[v].push_back(c);
			u = code(u, c), v = code(v, c); //编码
			e[u].push_back({ v, w });
			e[v].push_back({ u, w });
		}
		for (int i = 1; i <= n; ++i)
		{
			vector<int> &v = t[i];
			sort(v.begin(), v.end());
			v.erase(unique(v.begin(), v.end()), v.end());
			for (int j = 1; j < v.size(); ++j) //两个相邻编号建立额外边
			{
				int c1 = code(i, v[j - 1]), c2 = code(i, v[j]), w = v[j] - v[j - 1];
				if (i == 1 || i == n)
					w = 0; //起点终点的额外边权为0
				e[c1].push_back({ c2, w });
				e[c2].push_back({ c1, w });
			}
		}
		ll res = dijkstra();
		cout << res << endl;
		for (int i = 1; i <= idx; ++i) e[i].clear();
		for (int i = 1; i <= n; ++i) t[i].clear();
	}

	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值