洛谷 P1053 逛公园 解题报告
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\)行,每行一个整数代表答案。
数据规模与约定
对于不同的测试点, 我们约定各种参数的规模不会超过如下

对于 100%的数据, \(1\le P \le 10^9,1\le a_i,b_i \le N, 0\le c_i \le 1000\)。
数据保证:至少存在一条合法的路线。
记忆化搜索真是太优雅了,除了需要判一下第0层过1点的边
正题:
其实基本应该都可以猜到正解的复杂度应该是\(O(NKT)\)的
有向图代表我们可以按一定的顺序进行\(DP\)
而边权又都是整数,妥妥的把多走的路\(k\)和节点压进状态转移方程
我们先不考虑0环
\(dp[i][j]\)代表在点\(j\)时多走\(i\)的路程的方案数。
为什么特意把\(i\)放在第一维?因为\(i\)相当于那个大的循环,把\(i\)做完了才做\(i+1\)
这和平常不建多层图的分层图(即在跑最短路的时候维护\(dis[i][j]\)数组,代表\(i\)节点\(j\)层的信息)做法并不一样,因为那种分层图可以直接按权值大小来或者多次松弛
转移方程:\(dp[i][v]=\sum dp[i+dis[v]-edge[u][v]-dis[u]][u]\)
当然,我们的记忆化搜索得倒着做
我们得把每一层的\(j\)都分别做一遍,这样,正环也是没有影响的
0环呢?
我们维护一个搜索树的栈,代表这个点和深度的二元组还在搜索树中,如果这个二元组在搜索树中的时候又被访问,则存在0环。
值得一提的是,这样在第0层时是有问题的,它判不出来过1点的0环,需要特判
Code:
#include <cstdio>
#include <cstring>
#include <queue>
#define P pair <int,int >
using namespace std;
const int N=100010;
int head[N],edge[N<<2],to[N<<2],Next[N<<2],cnt;
void add(int u,int v,int w)
{
    edge[++cnt]=w;to[cnt]=v;Next[cnt]=head[u];head[u]=cnt;
}
int read()
{
    int x=0;char c=getchar();
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return x;
}
int n,m,k,p,t,dp[52][N],dis[N],used[N],vis[52][N],to0[N];
priority_queue <P,vector<P >,greater <P > > q;
P p0;
void disj()
{
    memset(dis,0x3f,sizeof(dis));
    memset(used,0,sizeof(used));
    dis[1]=0;
    p0.first=0,p0.second=1;
    q.push(p0);
    while(!q.empty())
    {
        int u=q.top().second;
        q.pop();
        if(used[u]) continue;
        used[u]=1;
        for(int i=head[u];i;i=Next[i])
        {
            if(i&1) continue;
            int v=to[i],w=edge[i];
            if(dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                p0.first=dis[v],p0.second=v;
                q.push(p0);
            }
        }
    }
}
void init()
{
    memset(head,0,sizeof(head));
    cnt=1;
    n=read(),m=read(),k=read(),p=read();
    int u,v,w;
    for(int i=1;i<=m;i++)
    {
        u=read(),v=read(),w=read();
        add(u,v,w),add(v,u,w);
    }
    disj();
    memset(dp,-1,sizeof(dp));
}
int flag=0;
void dfs0(int now)
{
    vis[0][now]=1;
    for(int i=head[now];i;i=Next[i])
        if((i&1)&&!edge[i])
        {
            if(vis[0][to[i]]) {flag=1;return;}
            dfs0(to[i]);
        }
    vis[0][now]=0;
}
int dfs(int now,int rest)
{
    if(vis[rest][now]) return -1;
    if(~dp[rest][now]) return dp[rest][now];
    vis[rest][now]=1;
    int v,w,res;
    for(int i=head[now];i;i=Next[i])
    {
        if(i&1)
        {
            v=to[i],w=edge[i];
            res=dis[now]+rest-dis[v]-w;
            if(res>=0)
            {
                if(dfs(v,res)==-1) return -1;
                (dp[rest][now]+=dfs(v,res))%=p;
            }
        }
    }
    vis[rest][now]=0;
    return ++dp[rest][now];
}
void work()
{
    memset(vis,0,sizeof(vis));
    int ans=0;
    dp[0][1]=1;dfs0(1);flag=0;
    if(flag) {printf("-1\n");return;}
    for(int i=0;i<=k;i++)
    {
        dp[i][n]=dfs(n,i);
        if(!(~dp[i][n])) {printf("-1\n");return;}
        (ans+=dp[i][n])%=p;
    }
    printf("%d\n",ans);
}
int main()
{
    t=read();
    while(t--)
    {
        init();
        work();
    }
    return 0;
}
2018.7.10
洛谷 P1053 逛公园 解题报告的更多相关文章
- 洛谷 P1053 篝火晚会 解题报告
		
P1053 篝火晚会 题目描述 佳佳刚进高中,在军训的时候,由于佳佳吃苦耐劳,很快得到了教官的赏识,成为了"小教官".在军训结束的那天晚上,佳佳被命令组织同学们进行篝火晚会.一共有 ...
 - 洛谷P3953 逛公园(NOIP2017)(最短/长路,拓扑排序,动态规划)
		
洛谷题目传送门 又是一年联赛季.NOIP2017至此收官了. 这个其实是比较套路的图论DP了,但是细节有点恶心. 先求出\(1\)到所有点的最短路\(d1\),和所有点到\(n\)的最短路\(dn\) ...
 - 洛谷P3953 逛公园 [noip2017] 图论+dp
		
正解:图论(最短路)+dp(记忆化搜索) 解题报告: 这题真的是个好东西! 做了这题我才发现我的dij一直是错的...但是我以前用dij做的题居然都A了?什么玄学事件啊...我哭了TT 不过其实感觉还 ...
 - 洛谷 P1783 海滩防御 解题报告
		
P1783 海滩防御 题目描述 WLP同学最近迷上了一款网络联机对战游戏(终于知道为毛JOHNKRAM每天刷洛谷效率那么低了),但是他却为了这个游戏很苦恼,因为他在海边的造船厂和仓库总是被敌方派人偷袭 ...
 - 洛谷  P2505 [HAOI2012]道路 解题报告
		
P2505 [HAOI2012]道路 题目描述 C国有n座城市,城市之间通过m条单向道路连接.一条路径被称为最短路,当且仅当不存在从它的起点到终点的另外一条路径总长度比它小.两条最短路不同,当且仅当它 ...
 - 洛谷 P4597 序列sequence 解题报告
		
P4597 序列sequence 题目背景 原题\(\tt{cf13c}\)数据加强版 题目描述 给定一个序列,每次操作可以把某个数\(+1\)或\(-1\).要求把序列变成非降数列.而且要求修改后的 ...
 - 洛谷1087 FBI树 解题报告
		
洛谷1087 FBI树 本题地址:http://www.luogu.org/problem/show?pid=1087 题目描述 我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全 ...
 - [NOIP2017] 逛公园 解题报告(DP)
		
我很不想说 在我的AC代码上我打了表,但实在没有办法了.莫名的8,9个点RE.然而即便是打表...也花了我很久. 这大概是NOIP2017最难的题了,为了让不懂的人更容易理解,这篇题解会比较详细 我的 ...
 - 洛谷 P3349 [ZJOI2016]小星星 解题报告
		
P3349 [ZJOI2016]小星星 题目描述 小\(Y\)是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品.她有\(n\)颗小星星,用\(m\)条彩色的细线串了起来,每条细线连着两颗小星星. 有一 ...
 
随机推荐
- LeetCode 192. Word Frequency
			
分析 写bash,不太会啊…… 难度 中 来源 https://leetcode.com/problems/word-frequency/ 题目 Write a bash script to calc ...
 - spring 在ssh三大框架中充当的角色
			
https://blog.csdn.net/yeah_nn/article/details/79992777
 - (一)Hyperledger Fabric 1.1安装部署-基础环境搭建
			
在学习和开发hyperledger fabric的时候遇到了一些坑,现将自己的一些总结和心得整理如下,以期对大家有所帮助.本次使用的宿主机环境:ubuntu,版本:Ubuntu 16.04.3 LTS ...
 - Flask之笔记集合
			
目录 一.简述 二.基本使用 三.配置文件 四.路由系统 2.自定义正则路由 五.模版语言 六.请求和响应 七.Session 2.自定义session 八.蓝图 九.message 十.中间件 十一 ...
 - 看oracle的sid
			
ps -ef|grep pmon 可以从进程名字里看到 也可以通过 sqlplus / as sysdbashow parameter instance_name
 - Git基础级介绍
			
这篇随笔是在学习了廖雪峰老师的git教程之后写的总结,要看详细的基础级git介绍可以去http://www.liaoxuefeng.com/wiki/0013739516305929606dd1836 ...
 - c#学习路线及目录导航
			
一 很久前的想法 转眼间,2018年已经过了四分之一,从我进入学校选择计算机专业到现在工作,已经过去了4年之久了.这一路走来经历了很多的曲折,对软件开发这个职业有了许多新的认识,我主要是从事NET领域 ...
 - 软工1816 · Beta冲刺(3/7)
			
团队信息 队名:爸爸饿了 组长博客:here 作业博客:here 组员情况 组员1(组长):王彬 过去两天完成了哪些任务 协助后端完成历史记录接口.美食排行榜接口 完成食堂平面图的绘制 确定web端业 ...
 - 【CSAPP笔记】10. 代码优化
			
写程序的主要目标是使它在所有可能的情况下都能正确运行(bug free),一个运行得很快但有 bug 的程序是毫无用处的.在 bug free 的基础上,程序员必须写出清晰简洁的代码,这样做是为了今后 ...
 - vue 选项卡(转载)
			
!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-e ...