1033 [NOIP2017]逛公园 记忆化搜索 比最短路长k的方案数 dp递推算方案数
作者:互联网
链接:https://ac.nowcoder.com/acm/contest/26077/1033
来源:牛客网
题目描述
策策同学特别喜欢逛公园。 公园可以看成一张 N 个点 M 条边构成的有向图,且没有自环和重边。其中 1 号点是公园的入口, N 号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要花的时间。策策每天都会去逛公园,他总是从 1 号点进去,从 N 号点出来。
策策喜欢新鲜的事物,他不希望有两天逛公园的路线完全一样,同时策策还是一个特别热爱学习的好孩子,他不希望每天在逛公园这件事上花费太多的时间。如果 1 号点到 N 号点的最短路长为 d,那么策策只会喜欢长度不超过 d + K 的路线。
策策同学想知道总共有多少条满足条件的路线,你能帮帮他吗?
为避免输出过大,答案对 P 取模。
如果有无穷多条合法的路线,请输出 −1。
输入描述:
第一行包含一个整数 T, 代表数据组数。i
接下来 T 组数据,对于每组数据:
第一行包含四个整数 N,M,K,P, 每两个整数之间用一个空格隔开。
接下来 M 行,每行三个整数 a
,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
输出
复制3 -1
说明
对于第一组数据,最短路为 3。
1 - 5, 1 - 2 - 4 - 5, 1 - 2 - 3 - 5 为 3 条合法路径。
备注:
对于不同测试点,我们约定各种参数的规模不会超过如下 本题在noip数据外额外增加一组数据卡掉了一种错解分析
又是一道dp递推求最短路径条数的问题,不过加了一个比最短路径长k 的路径都可以接受。
k不大只有50,所以可以开一个数组取求方案数
但是直接往前跑,在松弛操作 和 刚好到达最短路的时候 更新到某个点的最短路条数肯定是不行了,需要把每个 k 值的方案都一圈圈的绕出来,还要按照一定的拓扑序去算,非常麻烦。
不如直接记忆化搜索,记录到达当前点的 k 值,并向后,如果到达最终点 就返回一条路径,并累加递归回去。
这样就需要处理当前具体比最短路大了多少。
首先我们跑最短路的时候,从第一个点到当前点(设为v)的距离是可以算出来的 设为 w1
加上从当前点 v 到最终点 n 的最短路 长度 (设为 dv)即 dv + w1 ,再减去最短路长度 d 就是 k = dv + w1 - d
所以 k 是可以直接被处理的。
我们需要求出每个点到 n 的最小花费,直接从 n 点开始反向跑最短路就可以求出来了
另外:要注意有 0 环的存在
假设到了当前点 i ,且比最短路的长度 多 k,设为 flag[i][k]。
如果有0环 ,flag[i][k] 的情况就会出现两次以上。直接返回-1
//-------------------------代码---------------------------- #define int ll const int N = 1e5+10; int n,m,ki,mod; V<pii> g[N],fg[N]; int flag[N][60],dp[N][60]; int d[N]; bool vis[N]; void dij() { // 跑反向最短路 ms(vis,0);ms(d,0x3f) d[n] = 0; priority_queue<pii,V<pii>,greater<pii>>q; q.push({0,n}); int cnt = 0; while(q.size()) { auto tmp = q.top();q.pop(); int ver = tmp.y,w = tmp.x; if(w > d[ver]) { //当前走过的距离比最短距离大 continue; } for(int i = 0;i<fg[ver].size();i++) { int ww = fg[ver][i].second,u = fg[ver][i].first; if(d[u] > w + ww) { d[u] = w + ww; q.push({d[u],u}); } } } } int dfs(int v,int k) { if(flag[v][k]) {//存在0环 return dp[v][k] = -1; } if(dp[v][k]) { return dp[v][k]; } if(v == n) { dp[v][k] = 1; } flag[v][k] = 1; for(int i=0;i<g[v].size();i++) { int w = g[v][i].second,u = g[v][i].first; if(w - (d[v] - d[u]) <= k) { int pi = dfs(u,k-(w-(d[v]-d[u]))); if(pi == -1) { return dp[v][k] = -1; } dp[v][k] = (dp[v][k] + dp[u][k-(w-(d[v] - d[u]))]) % mod; } } flag[v][k] = 0; return dp[v][k]; } void solve() { cin>>n>>m>>ki>>mod; fo(i,1,n) {g[i].clear();fg[i].clear(); } ms(flag,0); fo(i,1,m) { int u,v,t;cin>>u>>v>>t; g[u].pb({v,t});fg[v].pb({u,t}); } dij(); ms(dp,0); cout<<dfs(1,ki)<<endl; } void main_init() {} signed main(){ AC();clapping();TLE; cout<<fixed<<setprecision(12); main_init(); // while(cin>>n,n) // while(cin>>n>>m,n,m) int t;cin>>t;while(t -- ) solve(); // {solve(); } return 0; } /*样例区 */ //------------------------------------------------------------
标签:NOIP2017,int,短路,flag,号点,策策,1033,dp 来源: https://www.cnblogs.com/er007/p/16611592.html