• 【题解】洛谷P3953 [NOIP2017TG] 逛公园(记忆化搜索+SPFA)


    题目来源:洛谷P3953

    思路

    先用SPFA求一遍最短路 

    在求最短路的同时可以把所有点到终点的最短路求出来 dis数组

    注意要反向SPFA  因为从起点开始可能会走到一些奇怪的路上导致时间负责度增加

    我们定一个f[u][k]数组为从当前节点u还剩时间k到达终点的方案

    原来从u走到终点的最短路径消耗时间为dis[u]

    而我们现在考虑走(u,v,w)这条边(不是最短路)

    那么比走最短路需要多dis[v]+w-dis[u]的时间

    所以f[u][k]=∑f[v][k-(dis[v]+w-dis[u])] (从u到终点上有另一个点v)

    记忆化搜索即可

    PS:容易错的地方是没有初始化

    代码

    #include<iostream>
    #include<cstring>
    #include<queue>
    using namespace std;
    #define maxn 100010
    #define INF 1e9+7
    int T,n,m,k,p,cnt,limit,ans;
    int dis[maxn],h1[maxn],h2[maxn],f[maxn][55];
    bool vis[maxn],v[maxn][55];
    struct Edge
    {
        int to;
        int next;
        int w;
    }e1[maxn*2],e2[maxn*2];
    void add(int u,int v,int w)
    {
        e1[++cnt].to=v;
        e1[cnt].w=w;
        e1[cnt].next=h1[u];
        h1[u]=cnt;
        e2[cnt].to=u;
        e2[cnt].w=w;
        e2[cnt].next=h2[v];
        h2[v]=cnt;
    }
    void spfa()//反向SPFA 
    {
        queue<int> q; 
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++) dis[i]=INF;
        q.push(n);
        dis[n]=0;
        vis[n]=1;
        while(!q.empty())
        {
            int temp=q.front();
            q.pop();
            vis[temp]=0;
            for(int i=h2[temp];i;i=e2[i].next)
            {
                int v=e2[i].to;
                if(dis[v]>dis[temp]+e2[i].w)
                {
                    dis[v]=dis[temp]+e2[i].w;
                    if(!vis[v])
                    {
                        vis[v]=1;
                        q.push(v);
                    }
                }
            }
        }
    }
    int dfs(int u,int k)
    {
        if(v[u][k]) return -1;//如果一条路上一个点第二次到达 说明有环 
        if(f[u][k]) return f[u][k];//记忆化 
        v[u][k]=1; 
        if(u==n) f[u][k]=1;//边界条件 到达终点还剩k步方案数为1 
        else f[u][k]=0;//否则为0 
        for(int i=h1[u];i;i=e1[i].next)
        {
            int v=e1[i].to;
            int temp=dis[v]-dis[u]+e1[i].w;//判断每一条路相比最短路是否超过k 
            if(temp<=k)//没超过 
            {
                int w=dfs(v,k-temp);//继续dfs 记得减去剩余时间 
                if(w==-1) return f[u][k]=-1;
                f[u][k]=(f[u][k]+w)%p;//记录方案数 
            }
        }
        v[u][k]=0;//重置点 
        return f[u][k];
    }
    int main()
    {
        cin>>T;
        while(T--)
        {
            cnt=0,ans=0;
            memset(h1,0,sizeof(h1));//记得初始化 
            memset(h2,0,sizeof(h2));
            memset(f,0,sizeof(f));
            memset(v,0,sizeof(v));
            cin>>n>>m>>k>>p;
            for(int i=1;i<=m;i++)
            {
                int x,y,z;
                cin>>x>>y>>z;
                add(x,y,z);
            }
            spfa();
            cout<<dfs(1,k)<<endl;//ans在dfs(1,k)中 
        }
    }
  • 相关阅读:
    oop第四次课作业总结
    我罗斯方块
    getline()、cin.getline()、cin.get()的区分和应用
    20200328上记笔记
    Markdown使用方法
    A Lovely Message Board
    NOIP2020游记
    题解 Codeforces Round #678 (Div. 2) (CF1436)
    CSP-S2020 第一轮认证(初赛)游记
    题解 Codeforces Round #670 (Div. 2) (CF1406)
  • 原文地址:https://www.cnblogs.com/BrokenString/p/9871549.html
Copyright © 2020-2023  润新知