Luogu 3953[NOIP2017] 逛公园 堆优化dijkstra + 记忆化搜索
题解
首先肯定是要求出单源最短路的,我用了堆优化dijikstra ,复杂度 mlogm,值得拥有!(只不过我在定义优先队列时把greater 打成了 less调了好久
然后我们就求出了$i$到源点的最短距离$dis_i$
定义一个数组 $f_{i, k}$表示从源点到节点$i$的距离比$dis_i$大$k$的路径数,另外一个数组$sch_{i,k}$ 记录某条路径上 该状态是否存在, 若在某条路径上出现了第二次, 并且$k <= K$,则可判断有符合条件的$0$环
对于要求的$f_{i, k}$ 需要求出它所有的前驱状态(也就是拓扑), 设它的某一前驱为 $f_{u,t}$则$t = dis_i + k - dis_u - w$ ,$w$为边权。
由于$t + dis_u + w = k + dis_i$, 所以$t - k = dis_i -dis_u - w$, 因为$dis_i$是到 $i$ 的最短距离, 则显然 $dis_i <= dis_u + w$, 得出$t <= k$, 也就是某个点前驱的 $k$值会不断减小,只需要$k >= 0$ 即可进行转移。
有转移方程 : $f_{i, k} = \sum f_{u,t}$ $t >= 0$。 边界为 $f_{1, 0} = 1$。但是若设$f_{1, 0} = 1$为边界状态的话,对于经过 点 $1$ 的0环就无法判断了。
所以我把边界定为$f_{0,0} = 1$,再从 $1 - > 0$连一条 记忆化搜索时需要记录前驱的反向,边权为0, 这样就可以解决$0$环的问题。
最后的答案$ans = \sum\limits_{k=0}^Kf_{n,k}$。
总时间复杂度为$O(MlogM + KN)$
代码
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<vector>
#define rd read()
using namespace std;
typedef pair<int,int> P;//用pair来存储dis 与点, 第一个关键字优先排序 const int N = 1e5 + 1e4; int n, m, K, T, head[N], tot, dis[N], f[N][], mod;
vector<int>q[N], w[N];//记录前驱的反向边 bool vis[N], sch[N][], flag = true; priority_queue<P ,vector<P>, greater<P> > pq;//优先队列 struct edge {
int u, v, c, nxt;
}e[N << ]; int read() {
int X = , p = ; char c = getchar();
for(; c > '' || c < ''; c = getchar() ) if( c == '-' ) p = -;
for(; c >= '' && c <= ''; c = getchar() ) X = X * + c - '';
return X * p;
} void add( int u, int v, int c ) {
e[++tot].u = u;
e[tot].v = v;
e[tot].c = c;
e[tot].nxt = head[u];
head[u] = tot;
} void dij() {//求单源最短路
memset( vis, , sizeof(vis));
memset( dis, 0x3f, sizeof(dis));
pq.push(P(, ));
dis[] = ;
for( P u, v; !pq.empty(); ) {
u = pq.top(); pq.pop();
int x = u.second;
if(vis[x]) continue;
vis[x] = ;
for( int i = head[x], nt; i; i = e[i].nxt ) if( dis[nt = e[i].v] > dis[x] + e[i].c && !vis[nt] ) {
dis[nt] = dis[x] + e[i].c;
pq.push(P(dis[nt], nt));
}
}
} int dp( int u, int k ) {//记忆化搜索
if( u == && k == ) return ;
if( f[u][k] != - ) return f[u][k];
f[u][k] = ;
sch[u][k] = ;
for( int i = ; i < (int)q[u].size(); ++i ) {
int nt = q[u][i], t = dis[u] + k - dis[nt] - w[u][i];
if(t < ) continue;
if( sch[nt][t] ) flag = false;//记录路径上的状态
f[u][k] = (f[u][k] + dp(nt, t)) % mod;
}
sch[u][k] = ;//回溯
return f[u][k];
} int main()
{
T = rd;
for(; T; T-- ) {
flag = true;
memset(f, -, sizeof(f));
memset(head, , sizeof(head));
memset(sch, , sizeof(sch));
tot = ;
for( int i = ; i <= n; ++i ) q[i].clear(), w[i].clear();
n = rd; m = rd; K = rd; mod = rd;
for( int i = ; i <= m; ++i ) {
int u = rd, v = rd, c = rd;
add(u, v, c);
q[v].push_back(u);
w[v].push_back(c);
}
q[].push_back();
w[].push_back();
dij();
dis[] = ;
int ans = ;
for( int i = ; i <= K; ++i ) ans = (ans + dp(n, i)) % mod;
if(!flag) printf("-1\n");
else printf("%d\n", ans);
}
}
Luogu 3953[NOIP2017] 逛公园 堆优化dijkstra + 记忆化搜索的更多相关文章
- [NOIP2017] 逛公园 (最短路,动态规划&记忆化搜索)
题目链接 Solution 我只会60分暴力... 正解是 DP. 状态定义: \(f[i][j]\) 代表 \(1\) 到 \(i\) 比最短路长 \(j\) 的方案数. 那么很显然最后答案也就是 ...
- HDU1142 (Dijkstra+记忆化搜索)
A Walk Through the Forest Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Jav ...
- luogu3953 [NOIp2017]逛公园 (tarjan+dijkstra+记忆化搜索)
先跑一边dijkstra算出从1到i的最短距离dis[i] 然后建反向边 从n开始记忆化搜索,(p,k)表示1到p的距离=dis[p]+k的方案数 答案就是$\sum\limits_{i=0}^{k} ...
- 洛谷3953 (NOIp2017) 逛公园——记忆化搜索+用栈判0环
题目:https://www.luogu.org/problemnew/show/P3953 因为K只有50,所以想到用dp[ cr ][ j ]表示在点cr.比最短路多走了 j 的方案数.(看了TJ ...
- HDU 1142 A Walk Through the Forest(Dijkstra+记忆化搜索)
题意:看样子很多人都把这题目看错了,以为是求最短路的条数.真正的意思是:假设 A和B 是相连的,当前在 A 处, 如果 A 到终点的最短距离大于 B 到终点的最短距离,则可以从 A 通往 B 处,问满 ...
- Luogu P2149 [SDOI2009]Elaxia的路线(最短路+记忆化搜索)
P2149 [SDOI2009]Elaxia的路线 题意 题目描述 最近,\(Elaxia\)和\(w**\)的关系特别好,他们很想整天在一起,但是大学的学习太紧张了,他们必须合理地安排两个人在一起的 ...
- Luogu P3953 [NOIP2017]逛公园
题目 首先我们跑出从\(1\)出发的最短路\(d1\)和反图上从\(n\)出发的最短路\(dn\). 然后我们处理出长度不超过\(d1_n+k\)的最短路边集,给它拓扑排序. 如果存在环,那么这个环一 ...
- 堆优化dijkstra
单源最短路径 题目链接:https://www.luogu.org/problemnew/show/P4779 直到做了这个题才发现我之前写的堆优化dijkstra一直是错的.. 这个堆优化其实很容易 ...
- Luogu P3953 逛公园(最短路+记忆化搜索)
P3953 逛公园 题面 题目描述 策策同学特别喜欢逛公园.公园可以看成一张 \(N\) 个点 \(M\) 条边构成的有向图,且没有自环和重边.其中 \(1\) 号点是公园的入口,\(N\) 号点是公 ...
随机推荐
- Oracle导出DMP文件的两种方法
本文转载自:http://www.cnblogs.com/o-andy-o/archive/2013/06/05/3118611.html 导出: 方法一:利用PL/SQL Developer工具 ...
- Sklearn数据集与机器学习
sklearn数据集与机器学习组成 机器学习组成:模型.策略.优化 <统计机器学习>中指出:机器学习=模型+策略+算法.其实机器学习可以表示为:Learning= Representati ...
- uva-10167-枚举
题意:生日蛋糕上面有2N草莓,怎么切能够将蛋糕和草莓平分成俩份,直接枚举,A和B,草莓不能落在直线上 #include <iostream> #include <stdio.h> ...
- B站上的一个MATLAB与神经网络的视频,捡漏
▶ av15514817.这里集中了一些从视频中学到的散点. ▶ 语句 "edit + 函数名" 可以打开部分内置函数的源代码.非公开的源代码这会打开一个全是注释的文档. ▶ 函数 ...
- HTML5 通过文件输入框读取文件为base64文件, 并借助canvas压缩 FileReader, files, drawImage
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...
- 使用AngularJS处理单选框和复选框的简单方法
在复选框中,可以绑定ng-model给false或者true值,即可选中或清除选中状态 如下图 而在单选框里就相对复杂一点,单选框要选中一个,就要给相同的name属性,然后绑定相同的ng-model, ...
- java ee7 软件安装和环境配置
1. java ee sdk 最新版下载地址 Java EE软件开发包(Software Development Kit, SDK) http://www.oracle.com/technetwork ...
- net send 换行和发送广播
net send ip message 要换行的时候按ctrl+t.最后按enter 或 ctrl + m 发出 如果是批处理里面要用: 在命令行下使用:echo ^T > a.txt,注意这里 ...
- fb 更新sdk
flash兼容flex.fb的sdk,但fb不一定兼容flash的sdk,那么直接将flash的sdk解压覆盖掉fb的sdk,就可以打开了. fb更新sdk方法: 1.找到(安装目录+eclipse\ ...
- 泛型集合与DataSet相互转换
一.泛型转DataSet /// <summary> /// 泛型集合转换DataSet /// </summary> /// <typeparam name=" ...