[刷题笔记] P9751 [CSP-J 2023] 旅游巴士

Problem_Link

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;
} 
posted @   SXqwq  阅读(122)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示