SP338 ROADS - Roads 题解

Update

  • \(\texttt{2020.11.9}\) 修改了一下公式。

Content

给定一个有 \(n\) 个点 \(r\) 条边的带权有向图,其中第 \(i\) 条边的起始点是 \(s_i\),终点是 \(d_i\),长度是 \(l_i\),花费是 \(t_i\)。求在总费用不超过 \(k\) 的情况下从 \(1\) 到 \(n\) 的最短路径长度。

数据范围:\(1\leqslant s_i,d_i\leqslant n,2\leqslant n\leqslant 100,1\leqslant r,k\leqslant10000,1\leqslant l_i\leqslant100,1\leqslant t_i\leqslant 100\)。

Solution

两维 dijkstra,有点类似于 DP。

给出以下定义:

\(dis_{i,j}\):从点 \(1\) 到 \(i\),且刚好花费 \(j\) 的情况下的最短距离。
\(vis_{i,j}\):是否在花费刚好为\(j\)的情况下经过点 \(i\),是为 \(1\),否为 \(0\)。

那我们不难想出这样的方程:\(dis_{v,j+cost_{i}}=dis_{i,j}+v_i\)(前提:\(j+cost_i\leqslant k\)),其中:

  • \(cost_i\) 代表经过第 \(i\) 条边的花费;
  • \(v_i\) 代表第 \(i\) 条边的长度;
  • \(k\) 如题意所述。

具体实现和 dijkstra 类似,注意多组数据要清空数组和队列(如果不使用堆优化则无需考虑该点),特判核心代码中括号内的情况。

非常简明的一道最短路题目,做起来需要多动脑筋。

Code

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <queue>
using namespace std;

int T, n, k, r, s, d, l, t, h[20007], cnt, dis[107][10007], vis[107][10007], ans;
struct edge {
	int v, cost, to, nxt;
}e[20007];
priority_queue<pair<int, pair<int, int> > > q;

inline void a_e(int u, int v, int w, int cost) {
	e[++cnt] = (edge){w, cost, v, h[u]}; h[u] = cnt;
}
inline int read() {
	int f = 1, x = 0;
	char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-')	f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9') {
		x = x * 10 + c - '0';
		c = getchar();	
	}
	return x * f;
}
inline int dijkstra() {
	for(int i = 1; i <= n; ++i)
		for(int j = 0; j <= k; ++j)
			dis[i][j] = 0x3fffffff;
	dis[1][0] = 0;
	q.push(make_pair(0, make_pair(0, 1)));
	while(!q.empty()) {
		int x1 = q.top().first, x2 = q.top().second.first, x3 = q.top().second.second;
		q.pop();
		if(vis[x3][x2])	continue;
		vis[x3][x2] = 1;
		for(int i = h[x3]; i; i = e[i].nxt) {
			int y = e[i].to, z = e[i].v, ko = e[i].cost;
			if(dis[y][x2 + ko] > dis[x3][x2] + z && x2 + ko <= k) {
				dis[y][x2 + ko] = dis[x3][x2] + z;
				if(!vis[y][x2 + ko])
					q.push(make_pair(-dis[y][x2 + ko], make_pair(x2 + ko, y)));
			}
		}
	}
	int ans = 0x3fffffff; 
	for(int i = 0; i <= k; ++i)
		ans = min(ans, dis[n][i]);
	return ans == 0x3fffffff ? -1 : ans;
}

int main() {
	scanf("%d", &T);
	while(T--) {
		memset(vis, 0, sizeof(vis));
		memset(e, 0, sizeof(e));
		memset(h, 0, sizeof(h));
		cnt = 0, ans = 0x3fffffff;
		while(!q.empty())	q.pop();
		scanf("%d%d%d", &k, &n, &r);
		for(int i = 1; i <= r; ++i) {
			scanf("%d%d%d%d", &s, &d, &l, &t);
			a_e(s, d, l, t);
		}
		printf("%d\n", dijkstra());
	}	
}
上一篇:【力扣算法之路】day2 746. 使用最小花费爬楼梯


下一篇:leetcode 21天动态规划入门——从0到0.5【Day02】爬楼梯