题意:
求有向图内$1$和$n$两点间路径计数,路径长度不超过最短路长度加$K$。
程序1(30分):
考虑记忆化搜索,设$dis_u$为点$n$到点$u$的最短路径长度,$f_{u,k}$表示$len(u,n)<=dis_u+k$的路径计数。
那么对于一条边$(u,v,w)$,一次转移中“耗费”的“额外路程”
$$len_{ex}=dis_v+w-dis_u$$
没什么好说,边取模边叠加即可。
考虑零环,发现等价于重复搜索,插旗即可。
然而神秘错误导致RE,得到30分。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<queue> #define IL inline #define UI unsigned using namespace std; const int inf=2147483647; const int N=1e5; const int M=2e5; const int K=50; int T,n,m,k,p; struct Edge{ int to,nxt,cap; }e[M+3],g[M+3]; int h[N+3],top,l[N+3],tpp; void add(int u,int v,int w,Edge *e,int *h,int &top){ top++; e[top].to=v; e[top].nxt=h[u]; e[top].cap=w; h[u]=top; } struct Dat{ int v,d; Dat(int a,int b) :v(a),d(b){} }; bool operator<(Dat a,Dat b){ return a.d>b.d; } priority_queue<Dat>que; int dis[N+3]; void dij(int S,Edge *e,int *h){ for(int i=1;i<=n;i++) dis[i]=inf; dis[S]=0; que.push(Dat(S,0)); while(!que.empty()){ Dat x=que.top(); que.pop(); while(!que.empty()&&dis[x.v]<x.d){ x=que.top(); que.pop(); } if(dis[x.v]<x.d) continue; int u=x.v; for(int i=h[u];~i;i=e[i].nxt){ int v=e[i].to; if(dis[u]+e[i].cap<dis[v]){ dis[v]=dis[u]+e[i].cap; que.push(Dat(v,dis[v])); } } } } int f[N+3][K+3]; bool vis[N+3][K+3]; int mod(int x){ if(x>=p) return x-p; return x; } int dfs(int u,int k){ if(vis[u][k]) return -1; if(f[u][k]>0) return f[u][k]; vis[u][k]=1; if(u==n) f[u][k]=1; else f[u][k]=0; for(int i=h[u];~i;i=e[i].nxt){ int v=e[i].to; int tmp=dis[v]-dis[u]+e[i].cap; if(tmp<=k){ int res=dfs(v,k-tmp); if(res==-1){ f[u][k]=-1; return -1; } f[u][k]=mod(f[u][k]+res); } } vis[u][k]=0; return f[u][k]; } int main(){ scanf("%d",&T); while(T--){ scanf("%d%d%d%d",&n,&m,&k,&p); memset(h,-1,sizeof h); memset(l,-1,sizeof l); top=tpp=-1; for(int i=1;i<=m;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); add(x,y,z,e,h,top); add(y,x,z,g,l,tpp); } dij(n,g,l); memset(f,0,sizeof f); memset(vis,0,sizeof vis); printf("%d ",dfs(1,k)); } return 0; }
程序2(100分):
思考RE来源,考虑越界爆栈,发现“额外路程”可能为负导致越界。
于是加了一句话,成功AC。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<queue> #define IL inline #define UI unsigned using namespace std; const int inf=2147483647; const int N=1e5; const int M=2e5; const int K=50; int T,n,m,k,p; struct Edge{ int to,nxt,cap; }e[M+3],g[M+3]; int h[N+3],top,l[N+3],tpp; void add(int u,int v,int w,Edge *e,int *h,int &top){ top++; e[top].to=v; e[top].nxt=h[u]; e[top].cap=w; h[u]=top; } struct Dat{ int v,d; Dat(int a,int b) :v(a),d(b){} }; bool operator<(Dat a,Dat b){ return a.d>b.d; } priority_queue<Dat>que; int dis[N+3]; void dij(int S,Edge *e,int *h){ for(int i=1;i<=n;i++) dis[i]=inf; dis[S]=0; que.push(Dat(S,0)); while(!que.empty()){ Dat x=que.top(); que.pop(); while(!que.empty()&&dis[x.v]<x.d){ x=que.top(); que.pop(); } if(dis[x.v]<x.d) continue; int u=x.v; for(int i=h[u];~i;i=e[i].nxt){ int v=e[i].to; if(dis[u]+e[i].cap<dis[v]){ dis[v]=dis[u]+e[i].cap; que.push(Dat(v,dis[v])); } } } } int f[N+3][K+3]; bool vis[N+3][K+3]; int mod(int x){ if(x>=p) return x-p; return x; } int dfs(int u,int k){ if(vis[u][k]) return -1; if(f[u][k]>0) return f[u][k]; vis[u][k]=1; if(u==n) f[u][k]=1; else f[u][k]=0; for(int i=h[u];~i;i=e[i].nxt){ int v=e[i].to; int tmp=dis[v]-dis[u]+e[i].cap; if(tmp<0) continue; if(tmp<=k){ int res=dfs(v,k-tmp); if(res==-1){ f[u][k]=-1; return -1; } f[u][k]=mod(f[u][k]+res); } } vis[u][k]=0; return f[u][k]; } int main(){ scanf("%d",&T); while(T--){ scanf("%d%d%d%d",&n,&m,&k,&p); memset(h,-1,sizeof h); memset(l,-1,sizeof l); top=tpp=-1; for(int i=1;i<=m;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); add(x,y,z,e,h,top); add(y,x,z,g,l,tpp); } dij(n,g,l); memset(f,0,sizeof f); memset(vis,0,sizeof vis); printf("%d ",dfs(1,k)); } return 0; }
小结:
流氓特判有点强。