1. 程式人生 > >洛谷3953:逛公園——題解

洛谷3953:逛公園——題解

記憶化搜索 其中 ref getch con blog 同學 min ons

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/ +

+++++++++++++++++++++++++++++++++++++++++++

洛谷3953:逛公園——題解