c++ 动态规划

动态规划(Dynamic Programming,简称 DP)是一种用于解决最优化问题的方法。其核心思想是通过将复杂问题分解为更小的子问题,保存子问题的解,并避免重复计算,以此提高效率。动态规划通常用于解决具有最优子结构重叠子问题性质的问题。

基本步骤

1. 确定状态:定义问题中的状态变量,这些状态变量通常代表问题的解的部分信息。

2. 状态转移方程:根据问题的性质,给出状态之间的关系,这通常是由已知状态推导出未知状态。

3. 初始化:设定初始状态。

4. 结果输出:根据计算得到的状态,返回最优解或所需结果。

应用示例

斐波那契数列

斐波那契数列的定义是:

F(0)=0, F(1)=1, F(n)=F(n−1)+F(n−2)  (n≥2)
#include <iostream>
#include <vector>
using namespace std;

int fib(int n) {
    if (n <= 1) return n;
    vector<int> dp(n + 1, 0);  // dp[i]表示第i个斐波那契数
    dp[0] = 0;
    dp[1] = 1;
    for (int i = 2; i <= n; ++i) {
        dp[i] = dp[i - 1] + dp[i - 2];
    }
    return dp[n];
}

int main() {
    int n;
    cout << "Enter n: ";
    cin >> n;
    cout << "Fibonacci of " << n << " is " << fib(n) << endl;
    return 0;
}

背包问题

给定一个背包容量为 W,有 n 个物品,每个物品的重量为 wi,价值为 vi。问如何选择物品,使得在不超过背包容量的情况下,背包中的物品的总价值最大。

#include <iostream>
#include <vector>
using namespace std;

int knapsack(int W, const vector<int>& weights, const vector<int>& values, int n) {
    vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0));  // dp[i][w]表示前i个物品,背包容量为w时的最大价值

    for (int i = 1; i <= n; ++i) {
        for (int w = 1; w <= W; ++w) {
            if (weights[i - 1] <= w) {
                // 可以选择当前物品或不选择
                dp[i][w] = max(dp[i - 1][w], dp[i - 1][w - weights[i - 1]] + values[i - 1]);
            } else {
                dp[i][w] = dp[i - 1][w];  // 不选择当前物品
            }
        }
    }
    return dp[n][W];  // 返回最大价值
}

int main() {
    int n, W;
    cout << "Enter number of items and knapsack capacity: ";
    cin >> n >> W;

    vector<int> weights(n), values(n);
    cout << "Enter weights and values: ";
    for (int i = 0; i < n; ++i) {
        cin >> weights[i] >> values[i];
    }

    cout << "Maximum value in knapsack: " << knapsack(W, weights, values, n) << endl;
    return 0;
}

最长公共子序列

给定两个字符串,求它们的最长公共子序列的长度。LCS 的定义是:在不改变字符顺序的前提下,删除一些字符使得剩下的字符是两个字符串的公共子序列。

#include <iostream>
#include <vector>
#include <string>
using namespace std;

int lcs(const string& s1, const string& s2) {
    int m = s1.length(), n = s2.length();
    vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));  // dp[i][j]表示s1[0..i-1]和s2[0..j-1]的LCS长度

    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;  // 如果相等,LCS长度加1
            } else {
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);  // 否则取最大值
            }
        }
    }
    return dp[m][n];  // 返回LCS的长度
}

int main() {
    string s1, s2;
    cout << "Enter two strings: ";
    cin >> s1 >> s2;

    cout << "Length of Longest Common Subsequence: " << lcs(s1, s2) << endl;
    return 0;
}

最短路径问题

在图中寻找从一个源点到所有其他点的最短路径,可以使用动态规划的思想结合贪心算法来解决。这里给出一个用 Dijkstra 算法求解单源最短路径的 C++ 实现。

#include <iostream>
#include <vector>
#include <climits>
#include <queue>
using namespace std;

typedef pair<int, int> pii;  // (距离, 节点)

void dijkstra(int n, int source, const vector<vector<pii>>& adj) {
    vector<int> dist(n, INT_MAX);
    dist[source] = 0;

    priority_queue<pii, vector<pii>, greater<pii>> pq;
    pq.push({0, source});

    while (!pq.empty()) {
        int u = pq.top().second;
        int d = pq.top().first;
        pq.pop();

        if (d > dist[u]) continue;

        for (const auto& edge : adj[u]) {
            int v = edge.first, w = edge.second;
            if (dist[u] + w < dist[v]) {
                dist[v] = dist[u] + w;
                pq.push({dist[v], v});
            }
        }
    }

    for (int i = 0; i < n; ++i) {
        if (dist[i] == INT_MAX)
            cout << "INF ";
        else
            cout << dist[i] << " ";
    }
    cout << endl;
}

int main() {
    int n, m;
    cout << "Enter number of nodes and edges: ";
    cin >> n >> m;

    vector<vector<pii>> adj(n);  // 邻接表
    cout << "Enter the edges (u, v, w): ";
    for (int i = 0; i < m; ++i) {
        int u, v, w;
        cin >> u >> v >> w;
        adj[u].push_back({v, w});
        adj[v].push_back({u, w});  // 如果是无向图
    }

    int source;
    cout << "Enter the source node: ";
    cin >> source;

    cout << "Shortest distances from source " << source << ": ";
    dijkstra(n, source, adj);

    return 0;
}

字符串编辑距离

字符串编辑距离(Edit Distance)问题,又称为Levenshtein 距离,是衡量两个字符串之间的相似度的标准。它表示将一个字符串转换成另一个字符串所需的最少编辑操作次数。

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;

// 动态规划实现字符串编辑距离
int minDistance(const string& s1, const string& s2) {
    int m = s1.length();
    int n = s2.length();
    
    // 创建一个二维dp数组,dp[i][j]表示将s1[0..i-1]转换为s2[0..j-1]的最小操作数
    vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));

    // 初始化边界条件:空字符串转换
    for (int i = 0; i <= m; ++i) dp[i][0] = i;  // 从s1转换到空字符串
    for (int j = 0; j <= n; ++j) dp[0][j] = j;  // 从空字符串转换到s2

    // 填充dp表格
    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];  // 字符相同,不需要任何操作
            } else {
                dp[i][j] = min({
                    dp[i - 1][j] + 1,  // 删除字符
                    dp[i][j - 1] + 1,  // 插入字符
                    dp[i - 1][j - 1] + 1  // 替换字符
                });
            }
        }
    }

    return dp[m][n];  // 返回最终的编辑距离
}

int main() {
    string s1, s2;
    cout << "Enter the first string: ";
    cin >> s1;
    cout << "Enter the second string: ";
    cin >> s2;

    int result = minDistance(s1, s2);
    cout << "The minimum edit distance is: " << result << endl;

    return 0;
}

总结

动态规划是一种强大的算法设计技巧,通过优化子问题的解来减少重复计算,显著提升算法效率。常见的应用场景包括:最短路径、背包问题、字符串匹配、图算法等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值