LOJ P2316 [NOIP2017] 逛公园【最短路】【记忆化搜索】

d i s [ i ] 表示 1 号点到 i 号点的最短距离,用 s p f a 跑一遍即可。

考虑如何设定状态。

注意到题意要的是小于等于 d i s [ n ] + K 的路径数量,而在我们跑完 s p f a 之后 d i s [ n ] 就是一个常数,而 K 的范围就比较和谐 50 ,所以状态从这里入手。

f [ i ] [ j ] 表示 1 > i 的路径中,路径长度比 d i s [ n ] j 的条数,设 k i 出边的另外一点,考虑边权转移得到状态转移方程:

f [ i ] [ j ] = f [ k ] [ d i s [ i ] d i s [ k ] e d g e [ i ] [ k ] + j ]

由于这样的状态并不好直接写几个 f o r 来转移,所以用记忆化搜索,搜索的同时记录一下某一个状态是否重复出现,如果重复出现了那就说明存在 0 环。

最后的答案就是:

A n s = i = 0 K f [ n ] [ i ]

#include <queue>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define DB double
#define SG string
using namespace std;
const int Max=1e5+5;
const int Inf=1e9;
int T,N,M,K,P,Ans,Flag,Vis[Max],Dis[Max],DP[Max][55],Vis_DP[Max][55];
struct Node{
    int Cnt,To[Max<<1],Edge[Max<<1],Next[Max<<1],Head[Max];
    void Init(){
        Cnt=0;
        memset(Head,0,sizeof(Head));
    }
    void Insert(int X,int Y,int Z){
        To[++Cnt]=Y;Edge[Cnt]=Z;Next[Cnt]=Head[X];Head[X]=Cnt;
    } 
    void SPFA(int S){
        int I,X;
        queue<int>Q;
        for(I=1;I<=N;I++){
            Vis[I]=0;
            Dis[I]=Inf;
        }Q.push(S),Vis[S]=1,Dis[S]=0;
        while(Q.size()){
            X=Q.front();
            Q.pop();
            Vis[X]=0;
            for(I=Head[X];I;I=Next[I]){
                int Y=To[I],Z=Edge[I];
                if(Dis[X]+Z<Dis[Y]){
                    Dis[Y]=Dis[X]+Z;
                    if(Vis[Y]==0){
                        Q.push(Y);
                        Vis[Y]=1; 
                    }
                }
            }
        }
    } 
}G1,G2;
inline int Read(){
    int X=0;char CH=getchar();bool F=0;
    while(CH>'9'||CH<'0'){if(CH=='-')F=1;CH=getchar();}
    while(CH>='0'&&CH<='9'){X=(X<<1)+(X<<3)+CH-'0';CH=getchar();}
    return F?-X:X;
}
inline void Write(int X){
    if(X<0)X=-X,putchar('-');
    if(X>9)Write(X/10);
    putchar(X%10+48);
}
void Clear(){
    Ans=0;Flag=1; 
    G1.Init();G2.Init();
    memset(DP,-1,sizeof(DP));
}
int DFS(int X,int K){
    int I,J;
    if(~DP[X][K]){
        return DP[X][K];
    }
    Vis_DP[X][K]=1;DP[X][K]=0;
    for(I=G2.Head[X];I;I=G2.Next[I]){
        int Y=G2.To[I],Z=Dis[X]-Dis[Y]+K-G2.Edge[I];
        if(Z>=0){
            if(Vis_DP[Y][Z]){
                Flag=0;
            }
            DP[X][K]+=DFS(Y,Z);DP[X][K]%=P;
        }
    } 
    Vis_DP[X][K]=0;
    return DP[X][K];
}
int main(){
    int I,J;
    T=Read();
    while(T--){
        Clear();
        N=Read(),M=Read(),K=Read(),P=Read();
        for(I=1;I<=M;I++){
            int X=Read(),Y=Read(),Z=Read();
            G1.Insert(X,Y,Z);G2.Insert(Y,X,Z); 
        }
        G1.SPFA(1);DP[1][0]=1;
        for(I=0;I<=K;I++){
            Ans+=DFS(N,I);Ans%=P;
        }
        DFS(N,K+1);
        if(!Flag){
            puts("-1");
        } else {
            printf("%lld\n",Ans); 
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yanzhenhuai/article/details/81481613