题目一:E - Train
题目链接:E - Train
给定 N 个编号为 1 至 N 的城市以及 M 条铁路。
第 i 条铁路连接城市 Ai 和 Bi,每当时间为 Ki 的倍数时会同时、分别从 Ai 和 Bi 发出开往对方的列车,列车从出发至到达花费 Ti 时间。
开始时你在城市 X,输出你到达城市 Y 的最早时间。若无法到达,输出 -1
。
忽略转车所需要的时间。即,当你 T 时刻到达某个城市时,可以立刻乘坐 T 时刻从这个城市发出的列车。
数据输入范围:
2 ≤ N ≤ 105 0 ≤ M ≤ 105
1 ≤ X,Y ≤ N X != Y 1 ≤ Ai,Bi ≤ N
Ai != Bi 1 ≤ Ti ≤ 109 1 ≤ Ki ≤ 109
单源最短路模板
1. 建图时,要将每一条边的花费时间,以及出发时间的倍数都保存下来,用于后面跑最短路。
2. 计算发车的时间
if(time % k == 0) return time;
return time + k - time % k; 查看time 是否是ki的倍数, 不是就将time变成最小的ki倍数值。
3. Dijkstra算法。
#include
using namespace std;
using i64 = long long;
using i128 = __int128;
using ui64 = unsigned long long;
int main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m, x, y;
cin >> n >> m >> x >> y;
vector>> g(n+1);
for(int i=1; i<=m; i++) {
int u, v, t, k;
cin >> u >> v >> t >> k;
g[u].push_back({v, t, k});
g[v].push_back({u, t, k});
}
priority_queue> q;
vector dis(n+1, LONG_LONG_MAX); // 开long long
auto go = [&](i64 time, int k)->i64{
if(time % k == 0) return time;
return time + k - time % k;
}; // 计算发车时间
dis[x] = 0;
q.push({0LL, x});
vectorvis(n+1);
while(!q.empty()) {
auto [time, u] = q.top();
q.pop();
if(vis[u])continue;
vis[u] = 1;
for( auto[v, t, k] : g[u]) {
i64 cur = go(-time, k);
if(dis[v] > cur + t) {
dis[v] = cur + t;
q.push({-dis[v], v}); // 大顶堆采用负数入堆
}
}
}
cout << (dis[y]==LONG_LONG_MAX? -1: dis[y]) << "\n";
return 0;
}
题目二:D - Wall
题目链接:D - Wall
题目大意:
你面前有一堵墙,墙上有数字,你需要将墙上的数字都变成 1
。
现在给出一个 W×H 的矩阵 A 表示墙上数字的情况。
其中若 Ai,j=−1 ,则表示位置 (i,j) 上没有数字,否则 Ai,j 的值表示墙上 (i,j) 位置的数字。
你还有一张 10×10 的表 C,其中 Ci,j 表示把数字 i 转化成数字 j 所需要的花费。求花费的最小值
具体题目见原题链接;
算法:Floyd 算法 模板
1. 先将矩阵C使用Floyd算法,跑出每一个的 数字i 到 j 的最短路。
2. 输入矩阵A时, 对于 1 和 -1 ,不用计算花费, 其他的 ans += dis[x][1].
#include
using namespace std;
using i64 = long long;
using i128 = __int128;
using ui64 = unsigned long long;
int main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
vector> dis(10,vector(10));
for(int i=0; i<=9; i++) {
for(int j=0; j<=9; j++) {
cin >> dis[i][j];
}
}
// 最短路
for(int k=0; k<=9; k++) {
for(int i=0; i<=9; i++) {
for(int j=0; j<=9; j++) {
if(dis[i][k] + dis[k][j] < dis[i][j])
dis[i][j] = dis[i][k] + dis[k][j];
}
}
}
int ans = 0;
for(int i=0; i> x;
if(abs(x)==1)continue; // 1, -1 不用
ans += dis[x][1];
}
}
cout << ans << "\n";
return 0;
}
题目三:E - Travel by Car
题目链接:E - Travel by Car
题目大意:
n 个小镇,m 条双向道路。第 i 条道路从 ai 通向 bi,长度为 ci。车的油箱容量为 L,当行驶到一个镇上时可以选择加满油或者什么都不做。行驶单位长度的距离消耗一单位的油。
现在回答 Q 个问题:
油箱现在为满,从 si 到 ti,最少需要加油多少次,或者输出-1。
2≤n≤300,0≤m≤n(n−1)/2,无重边,无自环。1≤ci,L≤1e9。
算法:Floyd算法 模板
1. 看数据量 Floyd 算法支持,可以使用。
2.做法, 先将可以用一次L升油的路给计算出来, 在这n个点上跑距离的最短路Floyd 也就是 dis矩阵
3.将两点之间小于 等于L 的距离要花费一桶油更新为1 , dis1矩阵
3.最后在 油上的花费再一次的跑一边 Floyd
4. 预处理答案, 如果油数为INT_MAX/2 则表示无法到达,记为-1, 否则减一(因为在开始车的状况为满油)
#include
using namespace std;
using i64 = long long;
using i128 = __int128;
using ui64 = unsigned long long;
int main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m, L;
cin >> n >> m >> L;
vector> dis(n+1,vector(n+1, LONG_LONG_MAX/2));
vector> dis1(n+1, vector(n+1, INT_MAX/2));
// 建边
for(int i=0; i> u >> v >> w;
dis[u][v] = w;
dis[v][u] = w;
}
// 先算点到点的距离最短路
for(int k=1; k<=n; k++) {
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
if(dis[i][j] > dis[i][k] + dis[k][j])dis[i][j] = dis[i][k] + dis[k][j];
}
}
}
// 统计那些点到点 可以只花费一次的 L 升油
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
if(dis[i][j]<=L)
dis1[i][j]=1;
}
}
// 最后在到花费的油上跑最短路
for(int k=1; k<=n; k++) {
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
if(dis1[i][j] > dis1[i][k] + dis1[k][j])dis1[i][j] = dis1[i][k] + dis1[k][j];
}
}
}
// 预处理答案
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
if(dis1[i][j]==INT_MAX/2)
dis1[i][j]=-1;
else dis1[i][j]--;
}
}
int q;
cin >> q;
while(q--) {
int u, v;
cin >> u >> v;
cout << dis1[u][v] << "\n";
}
return 0;
}
感谢收看与点赞, 欢迎大佬指正。