Luogu P3953 逛公园(最短路+记忆化搜索)

P3953 逛公园

题面

题目描述

策策同学特别喜欢逛公园。公园可以看成一张 \(N\) 个点 \(M\) 条边构成的有向图,且没有自环和重边。其中 \(1\) 号点是公园的入口,\(N\) 号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间。

策策每天都会去逛公园,他总是从 \(1\) 号点进去,从 \(N\) 号点出来。

策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果 \(1\) 号点到 \(N\) 号点的最短路长为 \(d\) ,那么策策只会喜欢长度不超过 \(d + K\) 的路线。

策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?

为避免输出过大,答案对 \(P\) 取模。

如果有无穷多条合法的路线,请输出 \(-1\) 。

输入输出格式

输入格式:

第一行包含一个整数 \(T\) ,代表数据组数。

接下来 \(T\) 组数据,对于每组数据: 第一行包含四个整数 \(N,M,K,P\) ,每两个整数之间用一个空格隔开。

接下来 \(M\) 行,每行三个整数 \(a_i,b_i,c_i\) ,代表编号为 \(a_i,b_i\) 的点之间有一条权值为 \(c_i\) 的有向边,每两个整数之间用一个空格隔开。

输出格式:

输出文件包含 \(T\) 行,每行一个整数代表答案。

输入输出样例

输入样例:

2
5 7 2 10
1 2 1
2 4 0
4 5 2
2 3 2
3 4 1
3 5 2
1 5 3
2 2 0 10
1 2 0
2 1 0

输出样例:

3
-1

说明

【样例解释1】

对于第一组数据,最短路为 \(3\) 。 \(1 - 5, 1 - 2 - 4 - 5, 1 - 2 - 3 - 5\) 为 \(3\) 条合法路径。

【测试数据与约定】

对于不同的测试点,我们约定各种参数的规模不会超过如下

测试点编号 \(T\) \(N\) \(M\) \(K\) 是否有 \(0\) 边
\(1\) \(5\) \(5\) \(10\) \(0\)
\(2\) \(5\) \(1000\) \(2000\) \(0\)
\(3\) \(5\) \(1000\) \(2000\) \(50\)
\(4\) \(5\) \(1000\) \(2000\) \(50\)
\(5\) \(5\) \(1000\) \(2000\) \(50\)
\(6\) \(5\) \(1000\) \(2000\) \(50\)
\(7\) \(5\) \(100000\) \(200000\) \(0\)
\(8\) \(3\) \(100000\) \(200000\) \(50\)
\(9\) \(3\) \(100000\) \(200000\) \(50\)
\(10\) \(3\) \(100000\) \(200000\) \(50\)

对于 $100 % $ 的数据, \(1 \le P \le 10^9,1 \le a_i,b_i \le N ,0 \le c_i \le 1000\) 。

数据保证:至少存在一条合法的路线。

思路

\(NOIP2017 / DAY1 / T3\)

首先我们求出每个点到 \(n\) 结点(公园出口)的距离,然后定义一个变量 \(dp[i][j]\) 表示比从 \(i\) 点出发到达 \(n\) 点的最短路长小于等于 \(j\) 的路径有多少条。然后我们逐步向后遍历整张图,求出的 \(dp[1][K]\) 就是题目要求的答案。遍历过程中我们可以用记忆化搜索的形式来优化时间复杂度。

那怎么判断无数多条合法情况呢?再定义一个变量 \(in[i][j]\) 表示在当前遍历过程中我们是否有在求 \(dp[i][j]\) 。如果有,说明搜索成环,直接返回 \(-1\) 。

具体实现代码如下:

LL dfs(LL now,LL k)
{
    if(in[now][k]) return -1;//成环
    if(dp[now][k]) return dp[now][k];//记忆化搜索
    in[now][k]=true,dp[now][k]=0;//开始搜索
    if(now==n) dp[now][k]=1;//搜到结尾时为一种情况
    for(LL i=top[now];i;i=nex[i])
    {
        LL delta=dis[to[i]]-dis[now]+len[i];//与最短路之间的差距
        if(delta<=k)
        {
            LL tmp=dfs(to[i],k-delta);
            if(tmp==-1) return -1;//成环
            dp[now][k]=(dp[now][k]+tmp)%p;
        }
    }
    in[now][k]=false;//结束搜索
    return dp[now][k];
}

函数调用入口就是:

printf("%lld\n",dfs(1,K));

怨念--;

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MAXN=100005;
LL T,n,m,K,p,dp[MAXN][55],dis[MAXN];
LL cnt,top[MAXN],to[MAXN<<1],len[MAXN<<1],nex[MAXN<<1];
LL __cnt,__top[MAXN],__to[MAXN<<1],__len[MAXN<<1],__nex[MAXN<<1];
bool vis[MAXN],in[MAXN][55];
LL read()
{
    LL re=0;
    char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
    return re;
}
void SPFA()
{
    memset(dis,0x3f,sizeof dis);
    memset(vis,false,sizeof vis);
    dis[n]=0;
    queue<LL>Q;
    Q.push(n);
    while(!Q.empty())
    {
        LL now=Q.front();Q.pop();
        vis[now]=false;
        for(LL i=__top[now];i;i=__nex[i])
            if(dis[__to[i]]>dis[now]+__len[i])
            {
                dis[__to[i]]=dis[now]+__len[i];
                if(!vis[__to[i]])
                {
                    vis[__to[i]]=true;
                    Q.push(__to[i]);
                }
            }
    }
}
LL dfs(LL now,LL k)
{
    if(in[now][k]) return -1;
    if(dp[now][k]) return dp[now][k];
    in[now][k]=true,dp[now][k]=0;
    if(now==n) dp[now][k]=1;
    for(LL i=top[now];i;i=nex[i])
    {
        LL delta=dis[to[i]]-dis[now]+len[i];
        if(delta<=k)
        {
            LL tmp=dfs(to[i],k-delta);
            if(tmp==-1) return -1;
            dp[now][k]=(dp[now][k]+tmp)%p;
        }
    }
    in[now][k]=false;
    return dp[now][k];
}
int main()
{
    T=read();
    while(T--)
    {
        n=read(),m=read(),K=read(),p=read(),cnt=__cnt=0;
        memset(dp,0,sizeof dp);
        memset(top,0,sizeof top);
        memset(__top,0,sizeof __top);
        memset(in,false,sizeof in);
        while(m--)
        {
            LL x=read(),y=read(),z=read();
            to[++cnt]=y,len[cnt]=z,nex[cnt]=top[x],top[x]=cnt;
            __to[++__cnt]=x,__len[__cnt]=z,__nex[__cnt]=__top[y],__top[y]=__cnt;
        }
        SPFA();
        printf("%lld\n",dfs(1,K));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/coder-Uranus/p/9742816.html

时间: 2024-10-08 02:46:58

Luogu P3953 逛公园(最短路+记忆化搜索)的相关文章

[Luogu P3953] 逛公园 (最短路+拓扑排序+DP)

题面 传送门:https://www.luogu.org/problemnew/show/P3953 Solution 这是一道神题 首先,我们不妨想一下K=0,即求最短路方案数的部分分. 我们很容易可以想到一个做法,就是魔改迪杰斯特拉做法: 如果一个点可以更新到达其他点的距离,那个点的方案数就是这个点的方案数:如果一个点所更新出来的距离和之前的相等,那个点的方案数加等当前点的方案数. 用式子可以表现为: f[j]=f[i] (dis[j]>dis[i]+x)   f[j]+=f[i] (dis

HDU 1142 A Walk Through the Forest(最短路+记忆化搜索)

A Walk Through the Forest Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 10172    Accepted Submission(s): 3701 Problem Description Jimmy experiences a lot of stress at work these days, especial

hdu 1428 漫步校园 (最短路+记忆化搜索)

漫步校园 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 3023    Accepted Submission(s): 917 Problem Description LL最近沉迷于AC不能自拔,每天寝室.机房两点一线.由于长时间坐在电脑边,缺乏运动.他决定充分利用每次从寝室到机房的时间,在校园里散散步.整个HDU校园呈方形布局,可划

POJ 2662-A Walk Through the Forest(最短路+记忆化搜索)

题目链接: 传送门 题意很重要.. 就是求求起点到终点按要求走有多少条路径.对于任意两点A,B,能从A走到B的条件是存在一条从B到终点的路的长度 小于任意一条A到终点的路,即B到终点的最短路小于A到终点的最短路.为什么呢?想一下,现在要在B到终点的路径中找出一条路满足它的长度小于 A到终点的最短路(这个好想,体会任意那两个字),所以当B到终点的最短路满足上述条件时,才满足最开始那个条件.(因为如果B到终点的最短路都不满足小于任意一条A到终点的路的话,那么其他的就更不满足了..) 然后就是dfs搜

hdu1142(最短路+记忆化搜索)

http://acm.hdu.edu.cn/showproblem.php?pid=1142 题目意思挺模糊 大致思路,以终点为源点,做一次单源最短路 深搜一遍图(下一步到达的位置  比现在位置  离终点更近) 记录每个节点上可行数 1 #include <bits/stdc++.h> 2 3 struct Edge 4 { 5 int v, w; 6 Edge(int _v, int _w): v(_v), w(_w){} 7 }; 8 9 const int MAXN = 1000 + 1

hdu 1142 最短路+记忆化

最短路+记忆化搜索 HDU 1142 A Walk Through the Forest 链接:http://acm.hdu.edu.cn/showproblem.php?pid=1142 题意:找出不同的路径条数,假如jimmy要从A走到B的话满足jimmy从B到家的距离比从A到家的距离短 这样我们可以通过最短路算法,找出从家(看成源点)到各个点的最短路径长度,记做D[v]. 然后就可以从起点(office) dfs,首先从某点i到某点j走得通,然后满足D[j] 1 #include<stdi

*HDU1142 最短路+记忆化dfs

A Walk Through the Forest Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 7995    Accepted Submission(s): 2943 Problem Description Jimmy experiences a lot of stress at work these days, especiall

hduoj----1142A Walk Through the Forest(记忆化搜索+最短路)

A Walk Through the Forest Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 5679    Accepted Submission(s): 2086 Problem Description Jimmy experiences a lot of stress at work these days, especiall

codeforces1202B You Are Given a Decimal String... 记忆化搜索或Floyd最短路算法

网址:http://codeforces.com/problemset/problem/1202/B 题意: 这个题真的挺绕的,草(中日双语),就是给出一串序列,然后你可以往里面填数,使得填数后的序列可以被$x-y$计数器输出.$x-y$计数器的工作方式是:初始是$0$,每一步加上$x$或者加上$y$,然后输出对$10$取余的数.输出$0<x,y<10$的$x-y$计数器下需要添加的字符数最小值,若无法满足输出$-1$. 题解: 草,比赛时靠着网上的资料一步步写出的记忆化搜索的方法,想不出正解