P9402 [POI2020-2021R3] Droga do domu
Description#
个点, 条边,无重边自环,边有长度。
1 号点是学校, 号点是家。
条公交线路。公交逢点必停,且一个点不会停两次。在一条边上行驶的时间就是它的长度。给定了第一班公交发车时间和发车间隔。
在时刻 从学校出发,至多换乘 次,求最早什么时候到家。
只计算路上时间和等车时间。换乘时间不计。
。
Solution#
一个图,求起点到终点的最小时间
但是怎么建图。如果直接在原图上面跑,是否换乘了这个问题将难以解决。但是注意到 。这意味着我们可以对于每条公交线,建 个点,拉成 条链。那么在这个链上转移就不需要换乘,从一条链到另一条链就换乘次数增加 。
这样的话,如果我们顺序枚举 ,就可以做到无后效性,从而 。
设 表示到了节点 ,此时换乘了 次的最小时间。注意这里的 的范围是 。
那么转移就可以从本链上的点不换乘转移,也可以从别的链上转移过来。
因为是链,所以每个点只会转移到一个点,而且链上节点的顺序是递增的。因此直接枚举 ,用 去更新 。
至于从别的链上转移,我们可以记录原图中节点 在新图中对应的节点是什么。在这些节点中找到最小的 ,去更新 。即使 被 更新也没有关系,因为换乘次数少的不会劣。
注意更新的顺序。要先更新了换乘的,再去更新不换乘的。这与坐车的顺序是一样的,先选择线路,再做到下一站。
时间复杂度 。
Code#
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define N 10005
#define M 50005
#define K 105
#define ll long long
using namespace std;
int n,m,s,k,cnt,rt[M];
ll t,inf,ans,f[M][K];
struct edg {int to;ll val;};
struct node
{
int to;
ll len;
ll st,ti;
}a[M];
struct que{int x,k;ll t;bool bj;};
vector<edg> g[N];
vector<int> p[N];
queue<que> q;
ll read()
{
ll res=0;char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch-'0'),ch=getchar();
return res;
}
void add(int x,int y,ll z,ll st,ll ti) {a[x].to=y;a[x].len=z;a[x].st=st;a[x].ti=ti;}
bool cmp(edg x,edg y) {return x.to<y.to;}
ll calc(ll nowt,ll st,ll ti)
{
if (nowt<=st) return st;
ll tn=(nowt-st)/ti;
if ((nowt-st)%ti!=0) tn++;
return tn*ti+st;
}
int main()
{
n=read();m=read();s=read();k=read();t=read();
for (int i=1;i<=m;++i)
{
int x,y;ll z;
x=read();y=read();z=read();
g[x].push_back((edg){y,z});
g[y].push_back((edg){x,z});
}
for (int i=1;i<=n;++i)
sort(g[i].begin(),g[i].end(),cmp);
for (int i=1;i<=s;++i)
{
int num=read();ll x=read(),y=read();
int lst=0,u=0;
for (int j=1;j<=num;++j)
{
int v;v=read();
++cnt;
p[v].push_back(cnt);rt[cnt]=v;
if (!u) u=v,lst=cnt;
else
{
int l=0,r=g[u].size()-1,mid,res=0;
while (l<=r)
{
mid=(l+r)>>1;
if (v<=g[u][mid].to) res=mid,r=mid-1;
else l=mid+1;
}
add(lst,cnt,g[u][res].val,x,y);
lst=cnt;x+=g[u][res].val;u=v;//找到链上每条边的权值
}
}
}
memset(f,127,sizeof(f));
inf=f[0][0];
for (int i=0;i<p[1].size();++i)
f[p[1][i]][0]=t;
for (int x=1;x<=cnt;++x)
{
int y=a[x].to;
if (!y) continue;
ll nxt=calc(f[x][0],a[x].st,a[x].ti)+a[x].len;
f[y][0]=min(f[y][0],nxt);
}
for (int j=1;j<=k;++j)
{
for (int now=1;now<=n;++now)
{
ll mn=inf;
for (int i=0;i<p[now].size();++i)
mn=min(mn,f[p[now][i]][j-1]);
for (int i=0;i<p[now].size();++i)
f[p[now][i]][j]=mn;
}//先换乘
for (int x=1;x<=cnt;++x)
{
int y=a[x].to;
if (!y) continue;
ll nxt=calc(f[x][j],a[x].st,a[x].ti)+a[x].len;
f[y][j]=min(f[y][j],nxt);
}//再坐到下一站
}
ans=inf;
for (int i=0;i<p[n].size();++i)
for (int j=0;j<=k;++j)
ans=min(ans,f[p[n][i]][j]);
if (ans>=inf) printf("NIE\n");
else printf("%lld\n",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本