Luogu P3953 [Noip2017]逛公园___记忆化搜索

题目大意;

一张N个点M条边构成的有向图,且没有自环和重边。
其中1号点是公园的入口,N号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要花的时间。
策策每天都会去逛公园,他总是从1号点进去,从N号点出来
策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果1号点到N号点的最短路长为d,那么策策只会喜欢长度不超过d+K的路线。
策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?
为避免输出过大,答案对P取模。
如果有无穷多条合法的路线,请输出−1。
数据保证:至少存在一条合法的路线。

Luogu P3953 [Noip2017]逛公园___记忆化搜索_第1张图片

题解:

先spfa求dis[i]为1到i的最短路
f[i][j]表示1到i的路径长度为dis[i]+j的路径有多少条。
f[1][0]=1
然后所有能到i的点,如k
则有
f[i][j]=∑f[k][dis[i]+j-dis[k]+cost[i][k]]
这里可以用记忆化去实现
d[i][j]表示i,j这个状态的存在情况,就是用来判断是否循环了,
不过因为f[1][0]=1,所以如果死循环中存在这个,就会卡掉我们的记忆化。。
所以我们最后一步要搜n,k+1
这一步可以判起点存在死循环的情况

代码:

#include
#include
#include
#define INF 23333333
#define N 100005
#define M 400005

using namespace std;

int next[M],ls[M],to[M],Next[M],Ls[M],To[M],w[M];
int dis[N],f[N][55],Q[2*M],T,n,m,k,p,cnt;
bool d[N][55],vis[N],check;

void add(int x,int y,int z)
{
     next[++cnt]=ls[x]; ls[x]=cnt; to[cnt]=y; w[cnt]=z;
     Next[cnt]=Ls[y]; Ls[y]=cnt; To[cnt]=x; 
}

void spfa(){
     int head=0,tail=1;
     vis[1]=0; dis[1]=0; Q[1]=1;
     while (headint u=Q[head];
            for (int i=ls[u]; i ;i=next[i])
            {
                int v=to[i];
                if (dis[v]>dis[u]+w[i]){
                    dis[v]=dis[u]+w[i];
                    if (!vis[v]){
                        vis[v]=1;
                        Q[++tail]=v;
                    }
                }
            }
            vis[u]=0;
     }
}

int dfs(int u,int rp){
    if (d[u][rp]){
        check=0;
        return 0;
    } 
    if(f[u][rp]!=-1) return f[u][rp];
    d[u][rp]=1;
    f[u][rp]=0;
    for(int i=Ls[u]; i ;i=Next[i])
    {
        int v=To[i];
        int cp=dis[u]+rp-dis[v]-w[i];
        if (cp>=0) f[u][rp]=(f[u][rp]+dfs(v,cp))%p;
    }
    d[u][rp]=0;
    return f[u][rp];
}

int main()
{
    scanf("%d",&T);
    while (T){
        scanf("%d%d%d%d",&n,&m,&k,&p);
        cnt=0;
        for (int i=1; i<=n; i++){
              for (int j=0; j<=k+1; j++) d[i][j]=0,f[i][j]=-1;
              dis[i]=INF;
              ls[i]=Ls[i]=vis[i]=0;
        }
        for (int i=1; i<=m; i++){
             int x,y,z;
             scanf("%d%d%d",&x,&y,&z);
             add(x,y,z);
        }
        spfa();
        f[1][0]=check=1;
        int ans=0;
        for (int i=0; i<=k; i++) 
             ans=(ans+dfs(n,i))%p;
        dfs(n,k+1);
        if (check) printf("%d\n",ans);
              else printf("-1\n");
        T--;
    }
    return 0;
}


你可能感兴趣的:(暴力/枚举/模拟,C++,动态规划)