首页 > 其他 > 详细

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

时间:2018-10-04 19:18:08      阅读:164      评论:0      收藏:0      [点我收藏+]

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;
}

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

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

踩
(0)
赞
(0)
   
举报
评论 一句话评论(0)
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!