这道题是noip2017原题。
首先,首先你可以思考一下,第一天dp不见了。似乎有很多人这么说。那么前两道题 我猜的 显然不是DP,所以说这道题可以用DP解。
但是我用的方法是记忆化搜索。因为DP这个东西 我太蒻了,不会 没有记忆化优美。
具体做法是跑一次反向的最短路,令f[u][k] 表示 dis(u,n)<=MinDis(u,n)+k的方案数,答案就是 f[1][K]。(从1号点到n好点不超过k的方案数)
考虑边(u,v,w)。
如果走这条边的话, dis(v,n)=MinDis(v,n)+w-MinDis(u,n) ⇒f[u][k]=∑f[v][k−(MinDis(v,n)−MinDis(u,n)+w)]
这样怎么判 0 环呢?只要加个标记判断一下就可以了。
还有一点要注意,就是必须先写一个函数判0环,再求值。不然的话可能会玄学死循环。
还有,就是f初值必须是1,赋值成0可能会原地爆炸。
代码实现:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<vector> #include<climits> #define int long long #define maxa 400005 #define mem(x) memset(x,0,sizeof(x)) #define mp(a,b) make_pair(a,b) using namespace std; void read(int &x){ x=0; char c=getchar(); while(c<'0'||c>'9'){ c=getchar(); } while(c<='9'&&c>='0'){ x=((x<<1)+(x<<3))+c-48; c=getchar(); } return ; } int n,m,k,mod; int to[maxa],nxt[maxa],h[maxa],top,val[maxa]; int To[maxa],Nxt[maxa],H[maxa],Top,Val[maxa]; void psh(int u,int v,int va){ to[++top]=v,nxt[top]=h[u],h[u]=top,val[top]=va; To[++Top]=u,Nxt[Top]=H[v],H[v]=Top,Val[Top]=va; return ; } int dis[maxa],vis[maxa]; void AC_DREAM(){//迪杰斯特拉 priority_queue <pair<int,int>,vector<pair<int,int> >,greater <pair<int,int> > >q ; q.push(mp(0,1)); while(!q.empty()){ int now=q.top().second;q.pop(); if(vis[now])continue; vis[now]=1; for(register int i=h[now];i;i=nxt[i]){ int y=to[i]; if(dis[y]>dis[now]+val[i])dis[y]=dis[now]+val[i],q.push(mp(dis[y],y)); } } return ; } int dp[maxa][51]; int bl[maxa][51]; bool yy; int num,y; int dfs(int x,int kk){//一定要写两遍,反正差不多,复制一下改一改就可以了。。。 if(~dp[x][kk])return dp[x][kk]; bl[x][kk]=1,dp[x][kk]=0; for(register int i=H[x];i;i=Nxt[i]){ y=To[i]; num=dis[x]-dis[y]+kk-Val[i]; if(num<0)continue; dp[x][kk]=(dp[x][kk]+dfs(y,num))%mod; } bl[x][kk]=0; return dp[x][kk]; } void dfs1(int x,int kk){//非常重要,必须分开写,不然玄学TLE if(yy)return ; if(~dp[x][kk])return ; bl[x][kk]=1,dp[x][kk]=0; for(register int i=H[x];i;i=Nxt[i]){ y=To[i]; num=dis[x]-dis[y]+kk-Val[i]; if(num<0)continue; if(bl[y][num]){ yy=1; return ; } dfs1(y,num); } bl[x][kk]=0; return ; } void clear(){ yy=Top=top=0; mem(to),mem(nxt),mem(h),mem(val); mem(To),mem(Nxt),mem(H),mem(Val); mem(vis); for(register int i=2;i<=n;i++)dis[i]=1000000000; mem(bl); } #undef int int main(){ #define int long long int T; read(T); while(T--){ read(n),read(m),read(k),read(mod); clear(); for(register int i=1,u,v,va;i<=m;i++){ read(u),read(v),read(va); psh(u,v,va); } AC_DREAM(); // for(int i=1;i<=top;i++)cout<<dis[i]<<" "; // cout<<endl; mem(vis); int ans=0; memset(dp,-1,sizeof(dp));//一定要赋值成-1! for(register int i=0;i<=k;i++)dfs1(n,i); if(yy){ cout<<-1<<endl; continue; } memset(dp,-1,sizeof(dp));//一定要赋值成-1! dp[1][0]=1; for(register int i=0;i<=k;i++)ans=(ans+dfs(n,i))%mod; cout<<ans<<endl; } }