洛谷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/ +
+++++++++++++++++++++++++++++++++++++++++++
洛谷3953:逛公園——題解