BFS的优先队列优化

出差

题目描述
小可要出差,拿出了地图进行研究。。。

地图上总共有N个城市,每个城市内都有各自的加油站,并且每个城市加油的价格也不尽相同。

N个城市之间拥有M条道路,可以通向不同的地方,所有的道路均是双向道路。

每条道路的长度均不相同,可以使用小可开车在这条道路上消耗的单位油量作为标注这条路的长度。

现在小可出差开着想在不同城市间穿行,请问最少需要花多少钱。

输入描述
第一行包含两个数字N和M,表示城市数量和道路数量。

第二行包含N个数字,为每个城市的单位油价。

然后输入M行,每行包含三个数字a,b,c,表示从a城市到b城市存在一条油耗为c的道路。

然后输入一行,包含一个整数K,表示K次询问。

然后输入K行,每行表示一次询问,每次询问包含三个整数q,w,e,为一辆容量为q的车子从w到e需最少需要花费多少钱。

输出描述
对于每个询问,输出最少的花费,若道路不通,则输出impossible。

输入样例
5 5
10 10 20 12 13
0 1 9
0 2 8
1 2 1
1 3 11
2 3 7
2
10 0 3
20 1 4
输出样例
170
impossible
提示 如图所示:
在这里插入图片描述

对于第一次询问:可以选择在0号点加满油,此时花费了100元,然后走到1号点,加7单位的油,花费70元,然后走到2号点,再走到3号点,到达目的地。

对于第二次询问:如图所示,肯定到不了。

数据范围
1≤N≤1000,0≤M≤10000

油价,油耗均不超过100,其余数据均合理

冷静分析

使用二元组(city,fuel)表示每个状态,其中city为城市编号,fuel为邮箱中剩余的油量,使用d[city][fuel]表示最少的费用。对于每个位置,都单独进行一次优先队列BFS,起始状态为(S,0)。每个状态的分支有:
1.若fuel<C,还没有装满油的话,可以选择可以加1升油,扩
展到新状态(city,fuel+1)
关于这个状态为什么是剩余油量+1相信有不少人和我WA了以后看题解一样的懵。其实解决的这个问题是有后效型的,你根本不知道在当前的加油站到底加多少油总花费才是最小的,所以干脆就枚举剩余油量,更新就是了

2.对于每一条边而言,我们可以通过这条边到达一个城市,那么
如果说当前剩余油量可以走完这条路,那么我们就拓展新状态(Next,fuel-w)

AC code~~~

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<iomanip>
#include<stack>
#include<map>
#include<queue>
#define TIE ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define N 1010
#define M 10010
#define INF 0x3f3f3f3f
using namespace std;
int head[N], nxt[M * 2], to[M * 2], val[M * 2], idx;
int n, m, Q, W, E;
int dis[N][N], vis[N][N], fuel[N];
void add(int u, int v, int w) {
	val[idx] = w, to[idx] = v, nxt[idx] = head[u], head[u] = idx++;
}
struct node {
	int cost, s, o;
	node(int a1, int b1, int c1): cost(a1), s(b1), o(c1) {}
	bool operator < (const node& hh)const {//这里是重定义,把大根堆变成按花费降序排列的小根堆~~~
		return cost > hh.cost;
	}
};
int dijkstra() {
	priority_queue<node> q;
	memset(dis, 0x3f, sizeof dis);
	memset(vis, 0, sizeof vis);
	q.push(node(0, W, 0));
	dis[W][0] = 0;
	while (q.size()) {
		node x = q.top();
		q.pop();
		if (x.s == E) return x.cost;
		if (vis[x.s][x.o]) continue;
		vis[x.s][x.o] = 1;
		if (x.o + 1 <= Q) {
			if (dis[x.s][x.o + 1] > x.cost + fuel[x.s]) {
				dis[x.s][x.o + 1] = x.cost + fuel[x.s];
				q.push(node(dis[x.s][x.o + 1], x.s, x.o + 1));
			}
		}
		for (int i = head[x.s]; i!=-1; i = nxt[i]) {//
			int y = to[i];
			if (x.o >= val[i]) {
				if (dis[y][x.o - val[i]] > x.cost) {
					dis[y][x.o - val[i]] = x.cost;
					q.push(node(dis[y][x.o - val[i]], y, x.o - val[i]));
				}
			}
		}
	}
	return -1;
}
int main() {
	TIE;cin >> n >> m;
	memset(head, -1, sizeof head);
	for (int i = 0; i < n; ++i) cin >> fuel[i];
	for (int i = 0; i < m; ++i) {
		int u, v, w;
		cin >> u >> v >> w;
		add(u, v, w);
		add(v, u, w);
	}
	int t;
	cin >> t;
	while (t--) {
		cin >> Q >> W >> E;
		int ans = dijkstra();
		if (ans == -1) cout << "impossible" << endl;
		else cout << ans << "\n";
	}
	return 0;
}

完结撒花~~~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值