[刷题笔记] P9751 [CSP-J 2023] 旅游巴士
Description
给定一个 \(n\) 个点,\(m\) 条边的有向图。起点为 \(1\) ,终点为 \(n\)。起始时间和终止时间必须是 \(k\) 的倍数。通过每条边的时间为 \(1\)。每条边有限制 \(a_i\) 即若通过当前边必须满足当前时间 \(t\geq a_i\)。求满足上述限制的前提下,到达终点的最小时间。若无法到达终点输出 \(-1\)。
对于所有测试数据有:\(2 \leq n \leq 10 ^ 4\),\(1 \leq m \leq 2 \times 10 ^ 4\),\(1 \leq k \leq 100\),\(1 \leq u _ i, v _ i \leq n\),\(0 \leq a _ i \leq 10 ^ 6\)。
Analysis
首先,要求 起始时间和终止时间必须是 \(k\) 的倍数。显然,等价于 起始时间和行走时间是 \(k\) 的倍数。
注意到通过每条边的时间为 \(1\)。考虑 BFS
全源最短路。
显然,由于众多限制。直接 BFS 状态无法合并,无法解决问题。
注意到 \(k\leq 100\)。考虑拆分状态,分层图最短路。将每个点拆分成 \(k\) 个状态。具体地,设 \(dist_{i,t}\) 表示点 \(i\) 当前时间为 \(t \% k\) 时的最短距离。对于边 \(\{u,v\}\),\(dist_{v,t}=min(dist_{v,t},dist_{u,(i+1)\% k})\)。
到这里是 \(\forall a_i =0\) 的情况。可以获得 \(35\) 分的好成绩。
35分代码
void bfs()
{
queue <PAIR> q;
q.push(PAIR(1,0));
dist[1][0] = 0;
while(!q.empty())
{
auto [u,i] = q.front();
q.pop();
for(auto [v,w]:Edge[u])
{
int j = (i+1)%k;
if(dist[v][j] == -1)
{
dist[v][j] = dist[u][i] + 1; //直接松弛即可
q.push({v,j});
}
}
}
}
接下来考虑每条边通过时间的限制。
由题意得,若当前时间 \(t \leq a_i\) 则无法通过该边。但是我们可以在原地停留 \(s+\lceil\frac{a_i-t}{k}\rceil\times k\) 时间。等价于将起始时间整体右移 \(\lceil\frac{a_i-t}{k}\rceil\) 个 \(k\)。因为我们需要保证起始时间是 \(k\) 的倍数。
然后就可以跑分层图最短路了。这里由于加入了时间的限制,我们类似于 Dijkstra 算法每次取当前时间最小的状态进行松弛。具体实现见代码。
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 10010;
const int INF = 0x3f3f3f3f;
struct Node
{
int u,i,w;
bool operator <(Node a)const
{
return a.w < w;
}
};
typedef pair<int,int> PAIR;
vector <PAIR> Edge[N];
int n,m,k;
int dist[N][1000];
bool vis[N][1000];
void bfs()
{
priority_queue <Node> q;
dist[1][0] = 0;
q.push({1,0,dist[1][0]});
while(!q.empty())
{
int u = q.top().u,i = q.top().i;
q.pop();
if(vis[u][i]) continue;
vis[u][i] = 1;
for(auto [v,w]:Edge[u])
{
int p = dist[u][i];
int j = (i+1)%k;
if(p < w) p+=(w-p+k-1)/k*k;
if(dist[v][j] > p+1)
{
dist[v][j] = p+1;
q.push({v,j,dist[v][j]});
}
}
}
}
signed main()
{
freopen("input.txt","r",stdin);
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>k;
int maxn = -1;
for(int i=1;i<=m;i++)
{
int u,v,w;
cin>>u>>v>>w;
Edge[u].push_back({v,w});
maxn = max(maxn,w);
}
memset(dist,0x3f,sizeof(dist));
bfs();
if(dist[n][0] > 1e9) dist[n][0] = -1; // 由于 memset 赋的值并不是 INF,所以判断时判最大答案即可。
cout<<dist[n][0]<<endl;
return 0;
}
本文作者:SXqwq,转载请注明原文链接:https://www.cnblogs.com/SXqwq/p/18019419
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!