调试的一晚上,终于AC了哈哈
其实就是 最短路+记忆化搜索
但是思考这道题的解法的时候想了很长时间,主要是因为 判零环逻辑 和 dp遍历方式 没搞好(想得十分复杂),最短路那一块反而没那么难
可以用vis数组标记当前处于遍历状态遍历的结点,如果遍历到合法的标记vis的状态,那么就说明存在零环,直接-1退出即可
这里非常想感慨一下,记忆化搜索真的很好用,尤其是在这种状态转移顺序与图的遍历密切相关的地方
最后放一下代码:
#include <bits/stdc++.h>
using namespace std ;
#define int long long
#define inf 0x3f3f3f3f3f3f3f3f
#define debug(x) cout << #x << ": " << x << endl
int n , m , k , p ;
const int maxN = 1e5+10 , maxM = 2e5+10, maxK = 51 ;
vector<array<int,2>> g[maxN] , rg[maxN] ;
bool dijkstra(int n,int s, int t, vector<int> &dist) {
vector<unsigned char> vis(n+1,false) ;
dist[s] = 0 ;
priority_queue<array<int,2>, vector<array<int,2>>, greater<array<int,2>>> que ;
que.push({0,s}) ;
while(!que.empty()) {
int u = que.top()[1] ; que.pop() ;
if(vis[u]) continue ;
vis[u] = true ;
for(auto &[v,w]:g[u]) {
if(dist[v]>dist[u]+w) {
dist[v] = dist[u] + w ;
que.push({dist[v],v}) ;
}
}
}
return vis[t] ;
}
int dfs(int u, int j, vector<vector<int>> &dp, vector<int> &dist, vector<vector<unsigned char>> &vis) {
if(dp[u][j]!=-1) return dp[u][j] ;
vis[u][j] = true ;
int res = 0 ;
for(auto &[v,w]:rg[u]) {
int vj = dist[u]+j-w-dist[v] ;
if(vj<0 || vj>k) continue ;
if(vis[v][vj])
return -1 ;
int add = dfs(v,vj,dp,dist,vis) ;
if(add == -1)
return -1 ;
res = (res + add)%p ;
}
vis[u][j] = false ;
return dp[u][j] = res+(u==1) ;
}
void solve() {
cin >> n >> m >> k >> p ;
for(int i=1 ; i<=n ; i++) {
g[i].clear() , rg[i].clear() ;
}
for(int i=1 ; i<=m ; i++) {
int u , v , w ; cin >> u >> v >> w ;
g[u].push_back({v,w}) ;
rg[v].push_back({u,w}) ;
}
vector<int> dist(n+1,inf) ;
if(!dijkstra(n,1,n,dist)) {
cout << 0 << endl ;
return ;
}
vector<vector<int>> dp(n+1, vector<int>(k+1,-1)) ;
vector<vector<unsigned char>> vis(n+1,vector<unsigned char>(k+1,false)) ;
int res = dfs(n,k,dp,dist,vis) ;
if(res == -1) {
cout << -1 << endl ;
return ;
}
cout << res << endl ;
}
signed main() {
int t ; cin >>t ;
while(t--) solve() ;
return 0 ;
}