首先,可以通过spfa求出最短路以及dis[i](表示1-i的最短路),然后考虑可以多走的距离为k,那么我们就可以枚举这一条边是否能被走,如果这条边连接i和j,那么就可以算出走这条边比原来最短路多走的距离为边权G[i].v-(d[i]-d[j]),然后就可以进行记忆化搜索,求总的方案
tips:这道题由于数组较大、T组数较多,用memset初始化会T两个点
代码
#include<bits/stdc++.h>
using namespace std;
int t,n,m,k,p;
const int maxn=100005;
const int inf=0x7FFFFFFF;
int cnt,tot,head[maxn],inin[maxn],ans[maxn][55],vis[maxn][55],tail[maxn],dis[maxn];
struct edge
{
int v,to,nxt;
}G[maxn<<1],reG[maxn<<1];
void add(int x ,int y ,int z)
{
G[++cnt].to=y; G[cnt].v=z; G[cnt].nxt=head[x]; head[x]=cnt;
}
void readd(int x ,int y ,int z)
{
reG[++tot].to=y; reG[tot].v=z; reG[tot].nxt=tail[x]; tail[x]=tot;
}
void spfa()
{
queue<int> q;
q.push(1);
dis[1]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i;i=G[i].nxt)
{
int to=G[i].to;
int v=G[i].v;
if(dis[to]>dis[u]+v)
{
dis[to]=dis[u]+v;
q.push(to);
}
}
}
}
void respfa()
{
queue<int> q;
q.push(n);
inin[n]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=tail[u];i;i=reG[i].nxt)
{
int to=reG[i].to;
if(!inin[to])
{
inin[to]=1;
q.push(to);
}
}
}
}
int dfs(int a, int kk)
{
if(kk<0) return 0;
if(vis[a][kk]==1) return -inf;
if(ans[a][kk]!=-1) return ans[a][kk];
vis[a][kk]=1;
int tmp=0;
if(a==n) tmp++;
for(int i=head[a];i;i=G[i].nxt)
{
int to=G[i].to;
int v=G[i].v;
int delta=dis[to]-dis[a];
if(!inin[to]) continue;
int temp=dfs(to,kk-(v-delta));
if(temp==-inf) return -inf;
tmp=(tmp+temp)%p;
}
ans[a][kk]=tmp;
vis[a][kk]=0;
return tmp;
}
/*
void clear()
{
for(int i=1;i<=n;i++)
{
for(int j=head[i];j;j=G[j].nxt)
G[j].nxt=0;
for(int j=tail[i];j;j=reG[j].nxt)
reG[j].nxt=0;
}
}*/
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%d",&t);
int x,y,z;
while(t--)
{
scanf("%d%d%d%d",&n,&m,&k,&p);
cnt=0; tot=0;
//clear();
for(int i=1;i<=n;i++)
{
head[i]=tail[i]=0;
inin[i]=0; dis[i]=inf;
for(int j=0;j<=k;j++)
ans[i][j]=-1,vis[i][j]=0;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z); readd(y,x,z);
}
spfa();
respfa();
int ans=dfs(1,k);
if(ans==-inf)
printf("-1\n");
else
printf("%d\n",ans);
}
return 0;
}