P9751 [CSP-J 2023] 旅游巴士 题解 最短路
思路:
题目说最后路径花费之和要是 \(k\) 的倍数,且 \(k\) 本身比较小,不难想到将原 Dijkstra 算法中的 d[v] 数组改写为 d[v][r],表示到点 \(v\) 时,路径花费对 \(k\) 取余结果为 \(r\) 时的最短路(vis 数组同理)。
这样答案即是 d[n][0]。问题在于松弛边的过程如何修改,我在思考时经历了以下三个问题:
-
到某个点的最短路不一定最优怎么办
例如:k=5,2->3这条边需要等待到时刻2- 路径 A:
1→2→3, 到2发现要等多一轮再进来才够,1+5+1=7 - 路径 B:
1→4→2→3, 到可以直接过,2+1=3
如果直接舍弃较长路径,就可能错过不需等待即可进入下一节点的机会。
解决: 因为转移的时候是将(v,r)组合作为状态,松弛时同时带上r。所以上面相当于是两个不同d[v][r]状态,不会相互影响。
松弛时如果发现当前边需要等待 \(t\) 单位才能通过,就相当于多等 \(\bigl\lceil \tfrac{t}{k}\bigr\rceil \times k\)。 - 路径 A:
-
舍弃同余更大花费的路径是否影响?
结论: 当两个不同路径到达(v,r),且余数相同,就算花费不同,也等价于在节点 \(v\) 时多等上若干个周期(+若干\(k\))。
所以无论选哪条,后续到达终点的总时间都不受影响。
比如\(k=2\)时
1->2->3->7,其中3->7是需要等到时刻4才能走的,所以需要等待一轮班次(+k),这条路径花费\(d[3]+2+1=2+2+1=5\),
但是如果走1->5->4->6->3->7,就可以直接通过,花费\(d[3]+1=4+1=5\)。该路径只是在3之前提前把k的花费给走了,效果相同。 -
有环路可“打发时间”会不会破坏? 如1->2,2->1
解决: 来回环路只会产生不同的余数状态,等跑到相同点且和之前同余时,此时多跑的正好等效于等待 \(k\) 的周期,由2可知等效。
代码:
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define endl '\n'
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn = 1e5+200;
const ll inf=0x3f3f3f3f3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+ 1) res=(res+k)%i;return (res+s)%n;}
inline ll read(){ ll f = 1; ll x = 0;char ch = getchar();while(ch>'9'||ch<'0') {if(ch=='-') f=-1; ch = getchar();}while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0', ch = getchar();return x*f; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };
const int V = 2e4+5, E=5e5+5;
ll d[V][105],cost[E];
ll n,m,k;
ll head[V],pnt[E],nxt[E],e=1;
void addedge(ll u,ll v,ll c)
{
pnt[e]=v;
cost[e]=c;
nxt[e]=head[u];
head[u]=e++;
}
int vis[V][101];
ll Dijkstra()
{
priority_queue<pair<ll,PII>, vector<pair<ll,PII>>, greater<pair<ll,PII>> > q;
d[1][0] = 0;
q.push({0LL,{1LL,0LL}});
while(!q.empty())
{
ll x = q.top().se.fi, r = q.top().se.se;
q.pop();
if(vis[x][r]) continue;
vis[x][r] = 1;
for(int i=head[x]; i; i = nxt[i])
{
ll v = pnt[i];
ll wait = max(cost[i]-d[x][r],0LL);
ll need = ((wait+k-1)/k)*k;
ll nr = (r+need+1)%k;
if(d[v][nr]>d[x][r]+r+need+1&&!vis[v][nr])
{
d[v][nr] = d[x][r] + need + 1;;
q.push({d[v][nr],{v,nr}});
}
}
}
return d[n][0]==inf?-1:d[n][0];
}
void sol()
{
n = read(), m = read(), k = read();
rep(i,0,n+1)
{
rep(j,0,k) d[i][j] = inf;
}
rep(i,1,m)
{
ll x = read(), y = read(), v = read();
addedge(x,y,v);
}
cout<<Dijkstra()<<endl;
}
int main()
{
sol();
return 0;
}

浙公网安备 33010602011771号