[NOIP2017]逛公园 最短路+拓扑排序+DP
[NOIP2017]逛公园
题目描述
策策同学特别喜欢逛公园。公园可以看成一张N个点M条边构成的有向图,且没有 自环和重边。其中1号点是公园的入口,N号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间。
策策每天都会去逛公园,他总是从1号点进去,从N号点出来。
策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果1号点 到N号点的最短路长为d,那么策策只会喜欢长度不超过d+K的路线。
策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?
为避免输出过大,答案对P取模。
如果有无穷多条合法的路线,请输出−1。
输入输出格式
输入格式:
第一行包含一个整数 T, 代表数据组数。
接下来T组数据,对于每组数据: 第一行包含四个整数 N,M,K,P,每两个整数之间用一个空格隔开。
接下来M行,每行三个整数ai,bi,ci,代表编号为ai,bi的点之间有一条权值为ci的有向边,每两个整数之间用一个空格隔开。
输出格式:
输出文件包含 T 行,每行一个整数代表答案。
输入输出样例
说明
【样例解释1】
对于第一组数据,最短路为 3。 1 – 5, 1 – 2 – 4 – 5, 1 – 2 – 3 – 5 为 3 条合法路径。
【测试数据与约定】
对于不同的测试点,我们约定各种参数的规模不会超过如下
测试点编号 | T | N | M | K | 是否有0边 |
---|---|---|---|---|---|
1 | 5 | 5 | 10 | 0 | 否 |
2 | 5 | 1000 | 2000 | 0 | 否 |
3 | 5 | 1000 | 2000 | 50 | 否 |
4 | 5 | 1000 | 2000 | 50 | 否 |
5 | 5 | 1000 | 2000 | 50 | 否 |
6 | 5 | 1000 | 2000 | 50 | 是 |
7 | 5 | 100000 | 200000 | 0 | 否 |
8 | 3 | 100000 | 200000 | 50 | 否 |
9 | 3 | 100000 | 200000 | 50 | 是 |
10 | 3 | 100000 | 200000 | 50 | 是 |
对于 100%的数据,1≤P≤109,1≤ai,bi≤N,0≤ci≤1000。
数据保证:至少存在一条合法的路线。
题解:本题的思路比较直接,沿着第一感觉一步一步做下去就可以了。
首先肯定要先跑最短路。然后呢?发现K只有50,所以我们一定是要从K入手。所以考虑DP,令f[i][j]表示走到i,多走的长度是j的方案数。(多走 指的是比最短路多的部分的长度)。
但是发现这个DP方程是存在环的,因为最短路径图上的边以及零边都是可以同行转移的(从f[..][j]转移到f[..][j]),所以怎么办呢?拓扑排序呗!
我们将最短路径图上的边以及零边都拿出来跑拓扑排序,然后让这些边在转移时必须沿着拓扑序转移即可。特别地,如果一个零环位于一条从1到n长度<=d+K的路径上,则输出-1即可。
代码有点丑了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | #include <cstdio> #include <cstring> #include <iostream> #include <queue> #include <algorithm> #include <utility> #define mp(A,B) make_pair(A,B) using namespace std; const int maxn=100010; const int maxm=200010; int n,m,cnt,K,P,tot,ans; int to[maxm],next[maxm],val[maxm],head[maxn],dis[maxn],vis[maxn],d[maxn],q[maxn<<1],f[51][maxn]; int to2[maxm],next2[maxm],val2[maxm],head2[maxn],dis2[maxn]; priority_queue<pair< int , int > > pq; inline int rd() { int ret=0; char gc= getchar (); while (gc< '0' ||gc> '9' ) gc= getchar (); while (gc>= '0' &&gc<= '9' ) ret=ret*10+gc- '0' ,gc= getchar (); return ret; } inline void add( int a, int b, int c) { to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt; to2[cnt]=a,val2[cnt]=c,next2[cnt]=head2[b],head2[b]=cnt++; } inline void upd( int &x, int y) { x=(x+y)%P; } void work() { n=rd(),m=rd(),K=rd(),P=rd(); int i,j,k,a,b,c,u; memset (head,-1, sizeof (head)), memset (head2,-1, sizeof (head2)),cnt=tot=ans=0; for (i=1;i<=m;i++) a=rd(),b=rd(),c=rd(),add(a,b,c); //1 memset (vis,0, sizeof (vis)), memset (dis,0x3f, sizeof (dis)), memset (d,0, sizeof (d)); pq.push(mp(0,1)),dis[1]=0; while (!pq.empty()) { u=pq.top().second,pq.pop(); if (vis[u]) continue ; vis[u]=1; for (i=head[u];i!=-1;i=next[i]) if (dis[to[i]]>dis[u]+val[i]) dis[to[i]]=dis[u]+val[i],pq.push(mp(-dis[to[i]],to[i])); } //2 memset (dis2,0x3f, sizeof (dis2)), memset (vis,0, sizeof (vis)); pq.push(mp(0,n)),dis2[n]=0; while (!pq.empty()) { u=pq.top().second,pq.pop(); if (vis[u]) continue ; vis[u]=1; for (i=head2[u];i!=-1;i=next2[i]) if (dis2[to2[i]]>dis2[u]+val2[i]) dis2[to2[i]]=dis2[u]+val2[i],pq.push(mp(-dis2[to2[i]],to2[i])); } //3 for (i=1;i<=n;i++) for (j=head[i];j!=-1;j=next[j]) if (dis[i]+val[j]==dis[to[j]]) d[to[j]]++; for (i=1;i<=n;i++) if (!d[i]) q[++tot]=i; for (j=1;j<=tot;j++) { u=q[j]; for (i=head[u];i!=-1;i=next[i]) if (dis[u]+val[i]==dis[to[i]]) { d[to[i]]--; if (!d[to[i]]) q[++tot]=to[i]; } } for (i=1;i<=n;i++) if (d[i]&&dis[i]+dis2[i]<=dis[n]+K) { printf ( "-1\n" ); return ; } //DP memset (f,0, sizeof (f)); f[0][1]=1; for (k=0;k<=K;k++) { for (i=1;i<=tot;i++) for (u=q[i],j=head[u];j!=-1;j=next[j]) if (dis[u]+val[j]==dis[to[j]]) { upd(f[k][to[j]],f[k][u]); } for (i=1;i<=n;i++) for (j=head[i];j!=-1;j=next[j]) if (dis[i]+val[j]!=dis[to[j]]&&k+dis[i]+val[j]-dis[to[j]]<=K) { upd(f[k+dis[i]+val[j]-dis[to[j]]][to[j]],f[k][i]); } } for (i=0;i<=K;i++) upd(ans,f[i][n]); printf ( "%d\n" ,ans); } int main() { //freopen("park.in","r",stdin); //freopen("park.out","w",stdout); int T=rd(); while (T--) work(); return 0; } |
| 欢迎来原网站坐坐! >原文链接<
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架