动态规划如何输出路径?

动态规划的大部分题目 都是不需要输出具体的路径的,但是万一要输出路径呢?
下面来看一下动态规划如何输出路径,以背包问题为例:

一般而言,背包问题是要求一个最优值,
如果要求输出这个最优值的方案(路径),可以参照一般动态规划问题输出方案的方法:记录下每个状态的最优值是由状态转移方程的哪一项推出来的,换句话说,记录下它是由哪一个策略推出来的。便可根据这条策略找到上一个状态,从上一个状态接着向前推即可。
这也就意味着 我们必须要维护一个状态数组 这个数组和DP数组一样,每个元素必须记录着这个状态是由哪个元素转移过来的。也就是说 我们要知道我们选择当前的坐标 是由哪个坐标跳过来的。所以我们在每个点就存上一个点的坐标。
或者我们选择哪个就把哪个坐标点标记为1 如果我们并不决定要走那个点 就把其重新覆盖为0.
输出字典序最小的最优方案:这样的话 跟我们选择的顺序有关系 --感觉这就完全改变了这个问题。解决方案并没有看懂
求次优解/第K优解: 我们的思路还是动态规划 状态转移方程也没有变化。唯一有变化是 针对每个点 我们不是只存一个当前最优值 而是存所有的可能的值(其实也不是所有可能的值 而是最大或者最小的k个值(至于为什么这样 见附录的解释)),将之放进PQ里面,每次要每个状态都试一遍。最后在数组的最后 输出PQ种poll出来的第K个元素即可。

附录:
为什么我们合并两个序列a b
假设第一个序列长为m 第二个为n
我们只需要取第一个序列前k个和第二个序列的前k个
合并得到的长度为2k的序列 这个序列一定包含a+b的前k个最大的元素。
为什么呢?

假设
a = [a1 a2 a3 a4 a5] 从大到小
b = [b1 b2 b3 b4 b5] 从大到小
k=3
我们取a的前三个和b的前3个 这六个元素一面一定包含 [a1 a2 a3 a4 a5 b1 b2 b3 b4 b5] (乱序)的前k大的元素。
证明:
[b1 + a1, b1 + a2, b1 + a3]就已经比[b1 + a4]大了 所以已经存在k个比b1+a4大的了 所以这个组合肯定没有机会进入前K大。所以每个取前k个就足够了。

refer:
dd大牛的背包九讲-背包问题汇总

动态规划求解最短路径通常使用Dijkstra算法,而不是输出所有路径。不过如果您确实需要输出所有路径,可以使用回溯法来实现。 假设我们有一个有向无环图,我们可以使用动态规划来求解从起点到终点的所有路径。 首先,我们需要建立一个存储路径的数据结构,例如一个vector。接着,我们定义一个二维数组dp[i][j],其中dp[i][j]表示从起点到第i个节点的所有路径中,长度为j的路径的数量。 我们可以使用以下递推式来计算dp[i][j]: dp[i][j] = 0; // 初始化 for (k = 0; k < n; k++) { if (graph[k][i] != 0) { for (l = 1; l <= j-1; l++) { if (dp[k][l] != 0) { for (m = 0; m < dp[k][l]; m++) { vector<int> tmp = paths[k][l][m]; tmp.push_back(i); paths[i][j].push_back(tmp); } } } } } if (graph[start][i] != 0) { paths[i][1].push_back(vector<int>(1, start)); dp[i][1] = 1; } 其中,n表示图中节点的数量,graph[i][j]表示从节点i到节点j的边的权重,start表示起点。 最后,我们可以遍历dp[end]中的所有路径,将它们输出。 完整代码如下: ```cpp #include <iostream> #include <vector> using namespace std; const int MAXN = 100; int graph[MAXN][MAXN]; int dp[MAXN][MAXN]; vector<vector<int>> paths[MAXN][MAXN]; void print_paths(int start, int end) { for (auto path : paths[end][dp[end]]) { if (path[0] == start) { for (int i = 0; i < path.size(); i++) { cout << path[i]; if (i != path.size() - 1) { cout << " -> "; } } cout << endl; } } } int main() { int n, m, start, end; cin >> n >> m >> start >> end; for (int i = 0; i < m; i++) { int u, v, w; cin >> u >> v >> w; graph[u][v] = w; } for (int i = 0; i < n; i++) { dp[i][0] = 1; } for (int j = 1; j <= n; j++) { for (int i = 0; i < n; i++) { dp[i][j] = 0; if (j == 1 && i == start) { paths[i][1].push_back(vector<int>(1, start)); dp[i][1] = 1; } else { for (int k = 0; k < n; k++) { if (graph[k][i] != 0) { for (int l = 1; l <= j-1; l++) { if (dp[k][l] != 0) { for (int m = 0; m < dp[k][l]; m++) { vector<int> tmp = paths[k][l][m]; tmp.push_back(i); paths[i][j].push_back(tmp); } } } } } } } } cout << "All paths from " << start << " to " << end << ":" << endl; print_paths(start, end); return 0; } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值