以下是使用C++语言描述矩阵连乘最优次序备忘录方法的代码:
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
const int MAX_N = 100;
int dp[MAX_N][MAX_N];
// 计算矩阵i到j的最小乘次数
int matrixChainMultiplication(vector<int>& matrix, int i, int j) {
if (i == j) {
return 0;
}
if (dp[i][j] != -1) {
return dp[i][j];
}
int res = INT_MAX;
for (int k = i; k < j; k++) {
int leftCost = matrixChainMultiplication(matrix, i, k);
int rightCost = matrixChainMultiplication(matrix, k + 1, j);
int currCost = matrix[i - 1] * matrix[k] * matrix[j];
res = min(res, leftCost + rightCost + currCost);
}
dp[i][j] = res;
return res;
}
int main() {
int n = 5;
vector<int> matrix = {10, 30, 5, 60, 20};
memset(dp, -1, sizeof(dp));
cout << "The minimum cost of matrix chain multiplication is: " << matrixChainMultiplication(matrix, 1, n - 1) << endl;
return 0;
}
在上述代码中,矩阵的维度信息保存在向量matrix中,matrix[i]表示第i个矩阵的行数,同时也是第i-1个矩阵的列数。备忘录数组dp用于保存已经计算过的结果,避免重复计算,初始化为-1。
该方法的时间复杂度为O(n^3),其中n为矩阵数量。这是因为在计算矩阵i到j的最小乘次数时,需要枚举k,因此总共有n^2个子问题需要计算,每个子问题需要O(n)的时间复杂度来计算,因此总时间复杂度为O(n^3)。同时,由于使用了备忘录方法,避免了重复计算,因此空间复杂度也为O(n^2)。
6.7.1.3
以下是使用C++语言描述矩阵连乘最优次序动态规划算法的代码:
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
const int MAX_N = 100;
int dp[MAX_N][MAX_N];
// 计算矩阵连乘的最小乘次数
int matrixChainMultiplication(vector<int>& matrix) {
int n = matrix.size() - 1;
for (int i = 1; i <= n; i++) {
dp[i][i] = 0;
}
for (int len = 2; len <= n; len++) {
for (int i = 1; i <= n - len + 1; i++) {
int j = i + len - 1;
dp[i][j] = INT_MAX;
for (int k = i; k < j; k++) {
int currCost = matrix[i - 1] * matrix[k] * matrix[j];
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + currCost);
}
}
}
return dp[1][n];
}
int main() {
int n = 5;
vector<int> matrix = {10, 30, 5, 60, 20};
cout << "The minimum cost of matrix chain multiplication is: " << matrixChainMultiplication(matrix) << endl;
return 0;
}
在上述代码中,矩阵的维度信息保存在向量matrix中,matrix[i]表示第i个矩阵的行数,同时也是第i-1个矩阵的列数。动态规划数组dp用于保存子问题的最优解,其中dp[i][j]表示从第i个矩阵到第j个矩阵的最小乘次数。
该方法的时间复杂度为O(n^3),其中n为矩阵数量。在计算矩阵连乘的最小乘次数时,需要枚举子问题的长度len、起点i和终点j,因此总共有n^3个子问题需要计算,每个子问题需要O(1)的时间复杂度来计算,因此总时间复杂度为O(n^3)。同时,由于使用了动态规划方法,不需要递归,因此空间复杂度为O(n^2)。
Like
Dislike
Share
6.7.1.4
以下是使用C++语言描述任意顶点间最短路径动态规划算法的代码:
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
const int MAX_V = 100;
int dp[MAX_V][MAX_V];
// 计算任意顶点i到j的最短路径
void floydWarshall(vector<vector<int>>& graph) {
int V = graph.size();
// 初始化dp数组
for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++) {
dp[i][j] = graph[i][j];
}
}
// 计算任意两点之间的最短路径
for (int k = 0; k < V; k++) {
for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++) {
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);
}
}
}
}
int main() {
int V = 4;
vector<vector<int>> graph = {
{0, 5, INT_MAX, 10},
{INT_MAX, 0, 3, INT_MAX},
{INT_MAX, INT_MAX, 0, 1},
{INT_MAX, INT_MAX, INT_MAX, 0}
};
floydWarshall(graph);
cout << "The shortest path between any two vertices is: " << endl;
for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++) {
if (dp[i][j] == INT_MAX) {
cout << "INF ";
} else {
cout << dp[i][j] << " ";
}
}
cout << endl;
}
return 0;
}
在上述代码中,图的邻接矩阵保存在二维向量graph中,graph[i][j]表示从顶点i到顶点j的边的权重,如果没有边相连,则为INT_MAX。动态规划数组dp用于保存任意两点之间的最短路径,其中dp[i][j]表示从顶点i到顶点j的最短路径长度。
该方法的时间复杂度为O(V^3),其中V为图的顶点数量。在计算任意两点之间的最短路径时,需要枚举中间节点k、起点i和终点j,因此总共有V^3个子问题需要计算,每个子问题需要O(1)的时间复杂度来计算,因此总时间复杂度为O(V^3)。同时,由于使用了动态规划方法,不需要递归,因此空间复杂度为O(V^2)。
6.7.1.5
以下是使用C++语言描述多段图动态规划算法的代码:
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
const int MAX_N = 100;
const int MAX_M = 100;
const int INF = INT_MAX;
// 计算多段图的最短路径
int multiStageGraph(vector<vector<int>>& graph, int n, int m) {
int dp[MAX_N];
// 初始化dp数组
for (int i = 0; i <= n; i++) {
dp[i] = INF;
}
// 最后一段到终点的距离为0
dp[n] = 0;
// 从后往前计算每个阶段的最短路径
for (int i = n - 1; i >= 1; i--) {
for (int j = 0; j < m; j++) {
if (graph[i][j] != INF) {
dp[i] = min(dp[i], graph[i][j] + dp[j + 1]);
}
}
}
return dp[1];
}
int main() {
int n = 4, m = 3;
vector<vector<int>> graph = {
{INF, INF, INF, INF},
{INF, INF, 2, 1},
{INF, 5, INF, 4},
{INF, INF, INF, INF},
{INF, INF, INF, INF}
};
cout << "The shortest path in the multi-stage graph is: " << multiStageGraph(graph, n, m) << endl;
return 0;
}
在上述代码中,多段图的邻接矩阵保存在二维向量graph中,graph[i][j]表示从第i段的顶点到第i+1段的顶点j的边的权重,如果没有边相连,则为INF。算法中使用一个一维数组dp来保存从当前阶段到终点的最短路径长度。
该方法的时间复杂度为O(nm),其中n为多段图的段数,m为每段中的顶点数。在计算每个阶段的最短路径时,需要枚举该阶段中的所有顶点,并在下一阶段中枚举所有可能的到达顶点,因此总共有nm个子问题需要计算,每个子问题需要O(1)的时间复杂度来计算,因此总时间复杂度为O(nm)。同时,由于使用了动态规划方法,不需要递归,因此空间复杂度为O(n)。
6.7.2.1
以下是使用C++语言描述求任意两个有限序列的最长公共子序列动态规划算法的代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 计算最长公共子序列的长度
int longestCommonSubsequence(string s1, string s2) {
int m = s1.size(), n = s2.size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (s1[i - 1] == s2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}
int main() {
string s1 = "AGGTAB";
string s2 = "GXTXAYB";
cout << "The length of the longest common subsequence is: " << longestCommonSubsequence(s1, s2) << endl;
return 0;
}
在上述代码中,字符串s1和s2为要比较的两个序列。算法中使用一个二维向量dp来保存计算结果,其中dp[i][j]表示s1的前i个字符和s2的前j个字符的最长公共子序列的长度。在计算dp[i][j]时,如果s1[i-1]等于s2[j-1],那么最长公共子序列中会包含s1[i-1]和s2[j-1],因此dp[i][j]等于dp[i-1][j-1]加上1;否则,最长公共子序列中不包含s1[i-1]和s2[j-1],因此dp[i][j]等于dp[i-1][j]和dp[i][j-1]中的较大值。
该方法的时间复杂度为O(mn),其中m和n分别为s1和s2的长度。在计算dp[i][j]时,需要枚举s1的前i个字符和s2的前j个字符,因此总共有mn个子问题需要计算,每个子问题需要O(1)的时间复杂度来计算。同时,由于使用了动态规划方法,不需要递归,因此空间复杂度为O(mn)。
6.7.2.2
以下是使用C++语言描述0/1背包问题动态规划算法的代码:
#include <iostream>
#include <vector>
#include <map>
using namespace std;
// 计算0/1背包问题的最大价值
int knapsack(vector<int>& w, vector<int>& v, int W) {
int n = w.size();
map<pair<int, int>, int> dp;
for (int i = 0; i <= n; i++) {
dp[make_pair(i, 0)] = 0;
}
for (int j = 1; j <= W; j++) {
dp[make_pair(0, j)] = 0;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= W; j++) {
if (w[i - 1] <= j) {
dp[make_pair(i, j)] = max(dp[make_pair(i - 1, j)], dp[make_pair(i - 1, j - w[i - 1])] + v[i - 1]);
} else {
dp[make_pair(i, j)] = dp[make_pair(i - 1, j)];
}
}
}
return dp[make_pair(n, W)];
}
int main() {
vector<int> w = {1, 2, 3};
vector<int> v = {6, 10, 12};
int W = 5;
cout << "The maximum value of 0/1 knapsack problem is: " << knapsack(w, v, W) << endl;
return 0;
}
在上述代码中,向量w和v分别表示物品的重量和价值,W表示背包的容量。算法中使用一个mapdp来保存计算结果,其中dp[make_pair(i, j)]表示前i个物品放入容量为j的背包中能够获得的最大价值。在计算dp[make_pair(i, j)]时,如果第i个物品的重量不超过j,那么可以选择将该物品放入背包中,此时dp[make_pair(i, j)]等于不放该物品的最大价值dp[make_pair(i-1, j)]和放该物品的最大价值dp[make_pair(i-1, j-w[i-1])] + v[i-1]中的较大值;否则,不能放该物品,此时dp[make_pair(i, j)]等于不放该物品的最大价值dp[make_pair(i-1, j)]。
该方法的时间复杂度为O(nW),其中n和W分别为物品的数量和背包的容量。在计算dp[make_pair(i, j)]时,需要枚举前i个物品和容量为j的背包,因此总共有nW个子问题需要计算,每个子问题需要O(1)的时间复杂度来计算。同时,由于使用了动态规划方法,不需要递归,因此空间复杂度为O(nW)。