最短路(road.pas/c/cpp)
题目大意
有一个点数为 n,边数为 m 的无向图,点的编号为 1 到 n。边的权值均为非负数。
现在请你求出从点 1 到点 n 的最短路径条数,若有无限条则输出-1,答案对于
1000000009 取模。
输入文件
输入文件为 road.in。
输入共有 m+1 行。
第一行有两个整数 n,m,表示图中所有的点数与边数。
接下来的 m 行,每行三个整数 x,y,z,表示了一条边的起点、终点以及该边的长度。
输出文件
输出文件为 road.out。
输出一个整数表示从点 1 至点 n 的最短路径条数,答案对于 1000000009 取模。
样例输入
4 4
1 2 1
1 3 1
2 4 1
3 4 1
样例输出
2
数据规模与约定
对于 10%的数据,n≤10,m≤10;
对于 40%的数据,n≤1000,m≤2000;
对于 100%的数据,n≤100000,m≤200000;
保证到任意一点最短路的长度不超过 64 位有符号长整型范围。
————————————————题解
这道题的出题人数据范围根本没有超过int,orz
本来写了从前面一次spfa,从后一次spfa,枚举每个点,本来能很暴力地过几个水水的点,结果没打cstring,memset挂编了……本机不给memset提示真是的
然后更难过的是回家写程序整理题的时候出题人出数据卡spfaQAQ,卡到了9s!!!太难过了,从此得到了一个教训就是复杂度能满足O((n+m)logn)的都用dijkstra吧,虽然有点朴素但人家是稳定的好算法,还可以线段树或者小根堆的这么愉快的搞一搞。
好的这道题的思路是建一个最短路图再在上面dp,当时有点懵,因为不知道啥叫最短路图,dp也写的很烂……
那么就这样把思路梳理一下吧,会了就好……
首先我们需要跑一遍最短路,对于这道题来说必须用Dijskra(因为出题人卡spfa),随手敲一个priority_queue优化也能过,评测的电脑很快(但我的机器很慢orz)
然后我们考虑建图,如果一条边(u->v)是起点到v的最短路上的路径,那么dis[v]-dis[u]=val(u->v),不难理解
然后我们挑出所有这样的边,就是一张图,而且是DAG(有向无环图),按照拓扑序来dp就可以,dp到n就是我们想要的答案,画张图来说明
事实上我们只要按拓扑序给当前节点指向的节点加上方案数的大小就可以了
如果这张图上有一条边为0,我们可以绕着0边走无限次,此时方案数为-1
如果这张图某个能到达n的节点(有些节点可能到达不了n)插着一条边为0,我们也可以走无限次,此时方案数为-1
【答案为-1的一个数据】
4 4
1 2 1
1 3 1
2 4 1
3 4 0
把这些0边搞掉之后来谈这个拓扑序,也不用求一遍,就是起点1到哪个点距离来排(最小的一定是1),再使用一次dis数组即可,因为不存在0边与负边,最短路的值从小到大即为拓扑序顺序
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <vector> 5 #include <queue> 6 #include <cmath> 7 #include <cstring> 8 #define siji(i,x,y) for(int i=(x);i<=(y);i++) 9 #define gongzi(j,x,y) for(int j=(x);j>=(y);j--) 10 #define xiaosiji(i,x,y) for(int i=(x);i<(y);i++) 11 #define sigongzi(j,x,y) for(int j=(x);j>(y);j--) 12 #define pil pair<int,long long> 13 #define pli pair<long long,int>//写了两个这个东西才觉得好蠢存边的时候值在前就可以很愉悦了…… 14 #define fi first 15 #define se second 16 #define mo 1000000009 17 #define lnf 9223372036854775807LL 18 //#define inf 0x7fffffff 19 using namespace std; 20 typedef long long ll; 21 int n,m; 22 vector<pil> g[100005]; 23 ll dis[100005]; 24 int ans[100005]; 25 int r[100005]; 26 void init() { 27 scanf("%d%d",&n,&m); 28 int x,y; 29 ll z; 30 siji(i,1,m) { 31 scanf("%d%d%lld",&x,&y,&z); 32 g[x].push_back(pil(y,z)); 33 g[y].push_back(pil(x,z)); 34 if(z==0) ans[x]=ans[y]=-1; 35 } 36 siji(i,1,n) {dis[i]=lnf;} 37 } 38 priority_queue<pli> q; 39 void dijskra(int u){ 40 41 dis[u]=0; 42 q.push(pli(0,1)); 43 while(!q.empty()) { 44 pli tmp =q.top(); 45 q.pop(); 46 if(-tmp.fi > dis[tmp.se]) continue; 47 int v=tmp.se; 48 int s=g[v].size()-1; 49 50 siji(j,0,s) { 51 if(dis[v]+g[v][j].se<dis[g[v][j].fi]) { 52 dis[g[v][j].fi]=dis[v]+g[v][j].se; 53 q.push(pli(-dis[g[v][j].fi],g[v][j].fi)); 54 //priority_queue默认大根堆,我们取反存就可以了,当你忘记了小值优先的算子的时候…… 55 } 56 } 57 } 58 59 } 60 bool cmp(int a,int b) { 61 return dis[a]<dis[b]; 62 } 63 64 inline void inc(int &a,int &b){a=a+b;while(a>=mo) a=a-mo;}//取模优化嘿嘿嘿 65 void graph() { 66 siji(i,1,n) r[i]=i; 67 sort(r+1,r+n+1,cmp);//排序最短路来求拓扑序 68 if(ans[1]==0) ans[1]=1; 69 siji(i,1,n) { 70 int t=g[r[i]].size()-1; 71 siji(j,0,t) { 72 if(dis[r[i]]+g[r[i]][j].se==dis[g[r[i]][j].fi]) { 73 if(ans[r[i]]==-1 || ans[g[r[i]][j].fi]==-1) { 74 ans[g[r[i]][j].fi]=-1;//在答案里标记一下插着0边的 75 } 76 else{ 77 inc(ans[g[r[i]][j].fi],ans[r[i]]); 78 } 79 } 80 } 81 } 82 } 83 84 void solve() { 85 dijskra(1); 86 graph(); 87 printf("%d ",ans[n]);//无限次的时候ans[n]就是-1了 88 } 89 int main(int argc, char const *argv[]) 90 { 91 freopen("road.in","r",stdin); 92 freopen("road.out","w",stdout); 93 init(); 94 solve(); 95 return 0; 96 }