P3953 逛公园

题目描述

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

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

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

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

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

如果有无穷多条合法的路线,请输出−1。

输入输出格式

输入格式:

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

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

接下来M行,每行三个整数ai​,bi​,ci​,代表编号为ai​,bi​的点之间有一条权值为 ci​的有向边,每两个整数之间用一个空格隔开。

输出格式:

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

输入输出样例

输入样例#1: 复制

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
输出样例#1: 复制

3
-1

说明

【样例解释1】

对于第一组数据,最短路为 3。 1 – 5, 1 – 2 – 4 – 5, 1 – 2 – 3 – 5为 33 条合法路径。

【测试数据与约定】

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

测试点编号   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≤P≤109,1≤ai​,bi​≤N,0≤ci​≤1000。

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


被誉为是noip2017最难的一道题了...以前写过,但那时基本就是对着标程抄,还没有理解。

对记忆化搜索本来做得不好,希望能有一些更深入的理解了吧....

首先观察数据范围,明显是一道与$K$有关的DP。可以想到定义$dp[u][k]$表示$u$到$n$的距离是$dis[u]+k$的方案数。所以答案就是$\sum_{i=0}^{k}{dp[1][i]}$所以还要建反向边预处理出每个点到$n$的最短路$dis$。

然后发现,正向跑时,$dp[u][k]$可以从所有它可以到达的$v$更新过来。

如图,已经确定了在$u$点时多出的$k$,那么如果要走到$v$点,可以确定$v$点多出的$k'$,通过$x+w-dis[u]=k$和$x-dis[v]=k'$可得出$k'=dis[u]+k-w-dis[v]$,然后就可以往下记忆化搜索来更新$dis[u][k]$了。初值$dp[u][0]=1$。

如何判0环?我们在搜索的时候定一个$fl[u][k]$标记,如果正在搜索中$fl=1$,如果搜索完了$fl=2$,如果同一个状态$[u][k]$第二次搜到的时候还在搜索中,即$fl[u][k]=1$,那么搜索过程中出现了0环,直接打标记退出即可。

#include<bits/stdc++.h>
using namespace std; int n, p, m, k; struct Node {
int v, nex, w;
Node(int v = , int nex = , int w = ) :
v(v), nex(nex), w(w) { }
} Edge[]; int h[], stot;
void add(int u, int v, int w) {
Edge[++stot] = Node(v, h[u], w);
h[u] = stot;
} int dis[], vis[];
void Spfa() {
queue < int > q;
memset(vis, , sizeof(vis));
memset(dis, 0x3f3f3f3f, sizeof(dis));
q.push(n); vis[n] = ; dis[n] = ;
while(!q.empty()) {
int u = q.front(); q.pop(); vis[u] = ;
for(int i = h[u]; i; i = Edge[i].nex) {
int v = Edge[i].v;
if(dis[v] > dis[u] + Edge[i].w) {
dis[v] = dis[u] + Edge[i].w;
if(!vis[v]) vis[v] = , q.push(v);
}
}
}
} int dp[][];
int fl[][], flag;
int dfs(int u, int k) {
if(fl[u][k] == || flag == -) return flag = -;
if(fl[u][k] == ) return dp[u][k];
fl[u][k] = ;
for(int i = h[u]; i; i = Edge[i].nex) {
int v = Edge[i].v;
int to = dis[u] + k - Edge[i].w - dis[v];
if(to > k || to < ) continue;
dp[u][k] = (dp[u][k] + dfs(v, to)) % p;
if(flag == -) return -;
}
fl[u][k] = ;
return dp[u][k];
} int a[], b[], c[];
int main() {
int T;
scanf("%d", &T);
while(T --) {
memset(h, , sizeof(h)); stot = ;
flag = ;
scanf("%d%d%d%d", &n, &m, &k, &p);
for(int i = ; i <= m; i ++) {
scanf("%d%d%d", &a[i], &b[i], &c[i]);
add(b[i], a[i], c[i]);
}
Spfa();
memset(h, , sizeof(h)); stot = ;
for(int i = ; i <= m; i ++)
add(a[i], b[i], c[i]);
int ans = ;
memset(fl, , sizeof(fl));
memset(dp, , sizeof(dp));
dp[n][] = ;
for(int i = ; i <= k; i ++)
ans = (long long)(ans + dfs(, i)) % p;
if(~flag) printf("%d\n", ans);
else printf("-1\n");
}
return ;
}

【洛谷】3953:逛公园【反向最短路】【记忆化搜索(DP)统计方案】的更多相关文章

  1. 2018.11.01 洛谷P3953 逛公园(最短路+dp)

    传送门 设f[i][j]f[i][j]f[i][j]表示跟最短路差值为iii当前在点jjj的方案数. in[i][j]in[i][j]in[i][j]表示在被选择的集合当中. 大力记忆化搜索就行了. ...

  2. Luogu 3953[NOIP2017] 逛公园 堆优化dijkstra + 记忆化搜索

    题解 首先肯定是要求出单源最短路的,我用了堆优化dijikstra ,复杂度 mlogm,值得拥有!(只不过我在定义优先队列时把greater 打成了 less调了好久 然后我们就求出了$i$到源点的 ...

  3. 洛谷 P1053 逛公园 解题报告

    P3953 逛公园 问题描述 策策同学特别喜欢逛公园. 公园可以看成一张\(N\)个点\(M\)条边构成的有向图,且没有自环和重边.其中1号点是公园的入口,\(N\)号点是公园的出口,每条边有一个非负 ...

  4. 洛谷P3953逛公园

    题目 作为\(NOIp2017D1T3\) 这个题还是很良心的,至少相对于\(NOIp2018\)来说,希望\(NOIp2019\)不会这么坑吧. 这个题可以作为记忆化搜索的进阶题了,做这个题的方法也 ...

  5. 洛谷P3953 逛公园

    DP+图论大毒瘤. 推荐这个博客. 先跑两遍最短路,搞掉一些无用点. 然后选出最短路上的边,做拓扑排序. 然后每层DP. 具体看代码. 用到的数组较多,记得清空. #include <cstdi ...

  6. 洛谷P3953 逛公园 [noip2017] 图论+dp

    正解:图论(最短路)+dp(记忆化搜索) 解题报告: 这题真的是个好东西! 做了这题我才发现我的dij一直是错的...但是我以前用dij做的题居然都A了?什么玄学事件啊...我哭了TT 不过其实感觉还 ...

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

    P3953 逛公园 题面 题目描述 策策同学特别喜欢逛公园.公园可以看成一张 \(N\) 个点 \(M\) 条边构成的有向图,且没有自环和重边.其中 \(1\) 号点是公园的入口,\(N\) 号点是公 ...

  8. UVA - 10917 - Walk Through the Forest(最短路+记忆化搜索)

    Problem    UVA - 10917 - Walk Through the Forest Time Limit: 3000 mSec Problem Description Jimmy exp ...

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

    A Walk Through the Forest Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Jav ...

  10. 洛谷P3953 逛公园(NOIP2017)(最短/长路,拓扑排序,动态规划)

    洛谷题目传送门 又是一年联赛季.NOIP2017至此收官了. 这个其实是比较套路的图论DP了,但是细节有点恶心. 先求出\(1\)到所有点的最短路\(d1\),和所有点到\(n\)的最短路\(dn\) ...

随机推荐

  1. Servlet笔记2--模拟Servlet本质、第一个Servlet程序、将响应结果输出到浏览器中

    以下代码均非IDE开发,所以都不规范,仅供参考 模拟Servlet本质: 模拟Servlet接口: /* SUN公司制定的JavaEE规范:Servlet规范 Servlet接口是Servlet规范中 ...

  2. Oracle Certified Java Programmer 经典题目分析(二)

    ...接上篇 what is reserved(保留) words in java? A. run B. default C. implement D. import Java 关键字列表 (依字母排 ...

  3. Linux内核抢占实现机制分析【转】

    Linux内核抢占实现机制分析 转自:http://blog.chinaunix.net/uid-24227137-id-3050754.html [摘要]本文详解了Linux内核抢占实现机制.首先介 ...

  4. ajax.BeginForm异步提交表单并显示更新数据

    view代码: <!--基本信息模块--> 2 <div class="profile_box" id="basicInfo"> 3 & ...

  5. bzoj 1034 泡泡堂BNB

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1034 题解: 很明显的贪心,读过田忌赛马的典故就很容易能想出来,分成三种情况讨论: < ...

  6. Pytorch多进程最佳实践

    预备知识 模型并行( model parallelism ):即把模型拆分放到不同的设备进行训练,分布式系统中的不同机器(GPU/CPU等)负责网络模型的不同部分 —— 例如,神经网络模型的不同网络层 ...

  7. 使用的vue、elementUI、vuex、express、mongoDB的单页应用

    基于vue.vuex.express.mongodb的一个单页应用,包括前后端,前端主要是使用vue,后端是node的express,数据库是使用的mongodb 1.下载使用 git clone h ...

  8. Runtime.getRuntime().exec 类 防止阻塞

    import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.IOException; impor ...

  9. Python基础:内置异常(未完待续)

    本文根据Python 3.6.5的官文Built-in Exceptions编写,不会很详细,仅对Python的内置异常进行简单(重难点)介绍——很多异常都可以从名称判断出其意义,罗列所有的内置异常. ...

  10. 数据库-mysql安装

    MySQL 安装 所有平台的Mysql下载地址为: MySQL 下载. 挑选你需要的 MySQL Community Server 版本及对应的平台. Linux/UNIX上安装Mysql Linux ...