这是一道京东笔试题目,属于动态规划中等难度题目

题目描述

第一行三个整数n,m,a(1≤n≤100,1≤m≤1000,1≤a≤1000),含义如题面所示。 接下来m行,第i行三个整数 $u_i$,$v_i$, $w_i(1 ≤ u_i,v_i \le n,1≤ w_i ≤ a)$,描述了一条道路。

输出描述

如果牛牛回家的方家数大于等于 20220201种,请你在第一行输出All roads lead to Home!,然后在第二行输出回家的方案数对 20220201 取模的结里

否则只需要输出一行一个整数,表示牛牛回家的方案数。

样例

输入

1
2
3
4
5
6
7
3 6 2
1 2 1
1 2 1
1 2 1
2 3 1
2 3 1
2 3 1

输出

1
9

说明

1
从城市一到城市二有3种不同的走法,从城市二到城市三也有3种不同的走法,根据乘法原理我们可以知道,一共有3x3=9种不同的回家方法。

题解

状态:

  • dp[i][j]表示从1到i花费j的方案数量
  • st[i][j]表示从1到i花费为j的方案数量是否超出20220201

转移方程:

dp[u][i + w] = (dp[u][i + w] + dp[v][i]) % INF;

其中更新路径由v到u,i是终点为v的花费

难点是为什么要把for (int i = 0; i < a; ++i) 写在外层?

假如写在内层,那就是算完一个节点的状态再算下一个节点的状态,同时已经算出的节点状态不会再次更新,但是如果题目的路径存在环,就导致节点状态会被反复更新,通过循环的方式不行,采用拓扑的遍历的方式同样也不行,因为拓扑遍历不能有环。
所以必须写在外层,写在外层为什么可以?写在外层是按花费更新的,花费每增加1就更新所有节点,这样必须保证如果是v更新u:

  • 那么v的状态一定是最终的正确状态,以后不会被其他节点再次更新
  • u被更新后不能在本次花费下作为v去更新其他节点

第一个条件是满足的,先看初始状态,dp[1][0]=1,只有1号节点初始花费为0有一种方案,其他节点花费为0的情况下是0。每个花费只能由花费小的来更新,所以如果遍历到m,那么所有节点小于m的花费状态一定作为v去更新了,那么m就是最终状态。

由于边权>0,所以dp[v][i]更新dp[u][j],j>i,那dp[v][j]不可能在本次花费下去更新别的节点了,因为本次花费是i。

然后就是如果最终的答案大于20220201就输出“所有路线均可”,由于计算的过程涉及取模,所以还需要定义一个st[i][j]表示从1到i花费为j的方案数量是否超出20220201,如果v更新了u,那么v是否超出模数的状态就会继承到u,这个还是比较好想的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>

const int INF = 20220201;
using namespace std;

int main() {
    int n, m, a;
    cin >> n >> m >> a;

    vector<vector<pair<intint>>> graph(n + 1); 
    for (int i = 0; i < m; ++i) {
        int vi, ui, wi;
        cin >> vi >> ui >> wi;
        graph[vi].emplace_back(ui, wi);
    }

    vector<vector<int>> dp(n + 1vector<int>(a + 10));
    vector<vector<bool>> st(n + 1vector<bool>(a + 10));
    dp[1][0] = 1
   
    for (int i = 0; i < a; ++i) {
        for (int v = 1; v <= n; ++v) {
            if (dp[v][i] > 0 || st[v][i]) {  
                for (auto &[u, w] : graph[v]) {
                    if (i + w <= a) {
                        dp[u][i + w] = (dp[u][i + w] + dp[v][i]) % INF;
                        if(dp[u][i + w] + dp[v][i] >= INF) st[u][i + w] = true;
                        if(st[v][i]) st[u][i + w] = true;
                    }
                }
            }
        }
    }
    if(st[n][a]) cout << "All roads lead to Home!" << endl;
    cout << dp[n][a] << endl; 
    return 0;
}