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());
}
}