洛谷3953:逛公园——题解
https://www.luogu.org/problemnew/show/P3953
策策同学特别喜欢逛公园。公园可以看成一张n个点m条边构成的有向图,且没有自环和重边。其中1号点是公园的入口,n号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要花的时间。
策策每天都会去逛公园,他总是从1号点进去,从n号点出来。
策策喜欢新鲜的事物,他不希望有两天逛公园的路线完全一样,同时策策还是一个特别热爱学习的好孩子,他不希望每天在逛公园这件事上花费太多的时间。如果1号点到n号点的最短路长为d,那么策策只会喜欢长度不超过d + k的路线。
策策同学想知道总共有多少条满足条件的路线,你能帮帮他吗? 为避免输出过大,答案对p取模。 如果有无穷多条合法的路线,请输出−1。
相信自己多做点dp题就能把思维提上来的我是不是很sb……
参考洛谷第一篇题解。
没记错当时奔着70pts去的结果30pts。
30pts很简单就不讲了。
70pts设f[i][j]为1~i路径长度比最短距离<=j的个数。
然后dij对dis处理之后对dis排序再进行更新。//我就是错在这个地方。
100pts其实0边也很好处理,拓扑找即可。
但是放在考场上会被卡常。
所以我们选择记忆化搜索。
但是如果普通的搜的话就又会犯我上面的错误。
思考错误的产生原因:即我们的偏移量k可能会出问题因为有些点有可能到不了n。
所以我们先dij反图处理点到n的距离,再在正图跑即可。
至于0环的判断,只要同一状态被dfs时搜过两次就一定是进了0环。
#include<cstdio> #include<iostream> #include<queue> #include<cstring> #include<algorithm> #include<cctype> using namespace std; const int N=100010; const int M=200010; const int K=51; const int INF=1e9; inline int read(){ int X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch=='-';ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } struct node{ int w,to,nxt; }e[2*M]; int n,m,k,p,cnt,head[N],dis[N],f[N][K]; bool vis[N],in[N][K]; struct cmp{ bool operator ()(int a,int b){ return dis[a]>dis[b]; } }; priority_queue<int,vector<int>,cmp>q; inline void add(int u,int v,int w){ e[++cnt].to=v;e[cnt].w=w;e[cnt].nxt=head[u];head[u]=cnt; } void bfs(int s){ dis[s]=0;vis[s]=1;q.push(s); while(!q.empty()){ int u=q.top();q.pop();vis[u]=0; for(int i=head[u];i;i=e[i].nxt){ if(i&1)continue; int v=e[i].to,w=e[i].w; if(dis[v]>dis[u]+w){ dis[v]=dis[u]+w; if(!vis[v]){ vis[v]=1;q.push(v); } } } } } int dp(int u,int t){ if(in[u][t])return f[u][t]=-1; if(f[u][t])return f[u][t]; if(u==n)f[u][t]=1; in[u][t]=1; for(int i=head[u];i;i=e[i].nxt){ if(!(i&1))continue; int v=e[i].to,w=e[i].w; int tmp=dis[v]+w-dis[u]; if(tmp<=t){ int tot=dp(v,t-tmp); if(tot==-1)return f[u][t]=-1; f[u][t]=(f[u][t]+tot)%p; } } in[u][t]=0; return f[u][t]; } inline void init(){ cnt=0; for(int i=1;i<=n;i++){ dis[i]=INF,vis[i]=head[i]=0; for(int j=0;j<=k;j++)f[i][j]=in[i][j]=0; } } int main(){ int t=read(); while(t--){ n=read(),m=read(),k=read(),p=read(); init(); for(int i=1;i<=m;i++){ int u=read(),v=read(),w=read(); add(u,v,w);add(v,u,w); } bfs(n); printf("%d\n",dp(1,k)); } return 0; }
+++++++++++++++++++++++++++++++++++++++++++
+本文作者:luyouqi233。 +
+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/ +
+++++++++++++++++++++++++++++++++++++++++++