其他分享
首页 > 其他分享> > 1033 [NOIP2017]逛公园 记忆化搜索 比最短路长k的方案数 dp递推算方案数

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, 代表数据组数。
接下来 T 组数据,对于每组数据:
第一行包含四个整数 N,M,K,P, 每两个整数之间用一个空格隔开。
接下来 M 行,每行三个整数 a
i
,b
i
,c
i
, 代表编号为 a
i
,b
i
 的点之间有一条权值为 c
i
 的有向边,每两个整数之间用一个空格隔开。

输出描述:

输出文件包含 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