逛公园
1.题目意思:
给一个有向图,求1—>n的路径中,路径长度小于dis[n]+k的路径数(min[i]表示1–>i的最短路长度);
2.解题思路
30分
对于k=0的数据考虑最短路计数即可
100分
设f[u][j]表示dis(1,u)(这里dis仅表示1—>u的任意路径长度)<=min[u]+k的路径总数
这个定义很关键,网上许多题解的定义并不明确,还是自己太菜。
显然min可以SPFA求出,对于f就得进行dp。
递推式很关键,看了许多题解都输草草概述,这里简单证明一下
对于u->v的边(原图正向)f[v]需要由f[u]递推而来,
定义f[u][j]: dis(1,u)<=min[u]+ju
f[v][j]: dis(1,v)<=min[v]+jv
还需要知道,走u–>v这条边对于min[v]来说多走了min[u]+len(边长)-min[v],画图感受一下即可
所以对于ju和jv有:
ju+min[u]+len-min[v]=jv,表示u—>v在u的基础上又多走了min[u]+len-min[v],于是移项得ju=jv+min[v]-min[u]-len
于是dp的递推式可以列出了:
对于u–>v
f
[
v
]
[
j
]
=
∑
u
−
>
v
f
[
u
]
[
j
+
m
i
n
[
v
]
−
m
i
n
[
u
]
−
l
e
n
]
f[v][j]= \sum_{u->v}^{}{f[u][j+min[v]-min[u]-len]}
f[v][j]=u−>v∑f[u][j+min[v]−min[u]−len]
于是就可以dp了,这里对于反向图进行记忆化搜索,为什么是反图呢,这样可以保证每一个便利的路程都可以抵达n,不会有到不了的情况进行多余的搜索
搜索的时候特别注意判断0环的情况,(即一个点被搜到了两次)
下面是代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#define MAXN 100010
using namespace std;
struct Edge{int to,l;};
vector<Edge> E[MAXN],UE[MAXN];
int T,n,m,p,k,dis[MAXN],vis[MAXN],f[MAXN][55],working[MAXN][55];
queue<int> q;
void SPFA(){
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
q.push(1); dis[1]=0; vis[1]=1;
while(!q.empty()){
int u=q.front(); q.pop(); vis[u]=0;
for(int i=0;i<E[u].size();i++){
int v=E[u][i].to,d=E[u][i].l;
if(dis[v]>dis[u]+d){
dis[v]=dis[u]+d;
if(!vis[v]) vis[v]=1,q.push(v);
}
}
}
}
int dfs(int u,int res){
int ans=0;
if(res<0||res>k) return 0;//不合法不应影响答案
if(working[u][res]){
working[u][res]=0;
return -1;
}//0环
if(f[u][res]) return f[u][res];//记忆化
working[u][res]=1;
for(int i=0;i<UE[u].size();i++){
int v=UE[u][i].to,d=UE[u][i].l;
int val=dfs(v,res-(dis[v]+d-dis[u]));
if(val==-1){
working[u][res]=0;
return -1;
}
ans=(ans+val)%p;
}//关键dp
working[u][res]=0;
if(u==1&&res==0) ans++;
f[u][res]=ans;
return ans;
}//以上回溯的时候将working置0,后面就不用memset了
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d%d%d",&n,&m,&k,&p);
for(int i=1;i<=n;i++) E[i].clear(),UE[i].clear();
for(int i=1;i<=m;i++){
int u,v,w; scanf("%d%d%d",&u,&v,&w);
E[u].push_back((Edge){v,w}),UE[v].push_back((Edge){u,w});
}
SPFA();//最短路
memset(f,0,sizeof(f));
int ans=0,flag=1;
for(int i=0;i<=k;i++){
int val=dfs(n,i);
if(val==-1){
flag=0;
break;
}
else ans=(ans+val)%p;
}
if(!flag) puts("-1");
else printf("%d\n",ans);
}
return 0;
}
谢谢